Database Normalization: 1NF, 2NF, 3NF, BCNF & Anomalies
Database normalization is a systematic process for organizing data in relational databases. The goal is to reduce redundancy and prevent anomalies during data manipulations.
What is Normalization?
Normalization breaks down complex data structures into smaller, logically related tables. By adhering to normal forms, data integrity and consistency are ensured.
Goals of Normalization
- Avoid redundancy: Store data only once
- Eliminate anomalies: Avoid update, insert, and delete anomalies
- Data integrity: Ensure consistent data
- Maintainability: Simple changes and extensions
Functional Dependencies
Basic Concepts
Functional dependency describes the relationship between attributes in a relation.
-- Beispiel: Studentenrelation
CREATE TABLE Studenten (
MatrikelNr INT PRIMARY KEY,
Name VARCHAR(50),
Semester INT,
Fachbereich VARCHAR(50)
);
-- Funktionsabhängigkeiten:
-- MatrikelNr → Name (jede Matrikelnummer hat genau einen Namen)
-- MatrikelNr → Semester (jede Matrikelnummer hat genau ein Semester)
-- MatrikelNr → Fachbereich (jede Matrikelnummer hat genau einen Fachbereich)
Types of Functional Dependencies
-- Vollständige Funktionsabhängigkeit
CREATE TABLE Noten (
MatrikelNr INT,
VorlesungNr INT,
Note DECIMAL(3,1),
PRIMARY KEY (MatrikelNr, VorlesungNr)
);
-- (MatrikelNr, VorlesungNr) → Note (vollständig abhängig)
-- MatrikelNr ↛ Note (nicht abhängig)
-- VorlesungNr ↛ Note (nicht abhängig)
-- Transitive Funktionsabhängigkeit
CREATE TABLE Dozenten (
DozentenNr INT PRIMARY KEY,
Name VARCHAR(50),
Fachbereich VARCHAR(50),
FachbereichLeiter VARCHAR(50)
);
-- DozentenNr → Fachbereich
-- Fachbereich → FachbereichLeiter
-- DozentenNr → FachbereichLeiter (transitiv)
Anomalies in Non-Normalized Data
Example of Anomalies
-- Nicht-normalisierte Tabelle mit Problemen
CREATE TABLE Probleme_Tabelle (
MatrikelNr INT,
StudentName VARCHAR(50),
VorlesungNr INT,
VorlesungName VARCHAR(50),
DozentNr INT,
DozentName VARCHAR(50),
Note DECIMAL(3,1),
Semester INT
);
-- Daten:
-- 101, 'Max Mustermann', 301, 'Datenbanken', 501, 'Prof. Schmidt', 1.3, 5
-- 101, 'Max Mustermann', 302, 'Algorithmen', 502, 'Prof. Mueller', 2.0, 5
-- 102, 'Erika Mustermann', 301, 'Datenbanken', 501, 'Prof. Schmidt', 1.7, 3
Types of Anomalies
1. Update Anomaly
-- Problem: Dozentenname ändern
UPDATE Probleme_Tabelle
SET DozentName = 'Prof. Dr. Schmidt'
WHERE DozentNr = 501;
-- Muss für alle Vorkommen gemacht werden!
2. Insert Anomaly
-- Problem: Neue Vorlesung ohne Studenten einfügen
INSERT INTO Probleme_Tabelle (MatrikelNr, VorlesungNr, VorlesungName, DozentNr, DozentName)
VALUES (NULL, 303, 'Software Engineering', 503, 'Prof. Weber');
-- Problem: MatrikelNr darf nicht NULL sein (Primary Key)
3. Delete Anomaly
-- Problem: Letzten Studenten aus Vorlesung löschen
DELETE FROM Probleme_Tabelle
WHERE MatrikelNr = 101 AND VorlesungNr = 302;
-- Verliert Information über Vorlesung 302 und Dozent 502
First Normal Form (1NF)
Definition and Rules
A relation is in 1NF if:
- All attributes are atomic (no repeating groups)
- Each row is uniquely identifiable (Primary Key)
- All attribute values come from the same domain
Implementation in 1NF
-- Vorher: Nicht in 1NF (wiederholte Gruppen)
CREATE TABLE Studenten_Vorher (
MatrikelNr INT PRIMARY KEY,
Name VARCHAR(50),
Vorlesungen VARCHAR(200), -- "301,302,303"
Noten VARCHAR(100) -- "1.3,2.0,1.7"
);
-- Nachher: In 1NF (atomare Attribute)
CREATE TABLE Studenten_1NF (
MatrikelNr INT PRIMARY KEY,
Name VARCHAR(50),
Semester INT
);
CREATE TABLE Belegungen_1NF (
MatrikelNr INT,
VorlesungNr INT,
Note DECIMAL(3,1),
PRIMARY KEY (MatrikelNr, VorlesungNr),
FOREIGN KEY (MatrikelNr) REFERENCES Studenten_1NF(MatrikelNr)
);
-- Beispiel für atomare Werte
INSERT INTO Studenten_1NF VALUES (101, 'Max Mustermann', 5);
INSERT INTO Belegungen_1NF VALUES (101, 301, 1.3);
INSERT INTO Belegungen_1NF VALUES (101, 302, 2.0);
1NF with JSON/XML (Modern Approaches)
-- PostgreSQL Beispiel mit JSON
CREATE TABLE Studenten_Modern (
MatrikelNr INT PRIMARY KEY,
Name VARCHAR(50),
Belegungen JSONB
);
INSERT INTO Studenten_Modern VALUES (
101,
'Max Mustermann',
'[
{"vorlesungNr": 301, "note": 1.3},
{"vorlesungNr": 302, "note": 2.0}
]'
);
-- Abfrage mit JSON-Funktionen
SELECT
MatrikelNr,
Name,
vorlesung->>'vorlesungNr' AS Vorlesung,
vorlesung->>'note' AS Note
FROM Studenten_Modern,
jsonb_array_elements(Belegungen) AS vorlesung
WHERE MatrikelNr = 101;
Second Normal Form (2NF)
Definition and Rules
A relation is in 2NF if:
- It is in 1NF
- All non-key attributes depend completely on the primary key
Problem: Partial Dependencies
-- Problem: Nicht in 2NF (partielle Abhängigkeiten)
CREATE TABLE Belegungen_Problem (
MatrikelNr INT,
VorlesungNr INT,
StudentName VARCHAR(50), -- Hängt nur von MatrikelNr ab
VorlesungName VARCHAR(50), -- Hängt nur von VorlesungNr ab
DozentNr INT, -- Hängt nur von VorlesungNr ab
Note DECIMAL(3,1), -- Hängt von (MatrikelNr, VorlesungNr) ab
PRIMARY KEY (MatrikelNr, VorlesungNr)
);
-- Partielle Abhängigkeiten:
-- MatrikelNr → StudentName (partiell)
-- VorlesungNr → VorlesungName, DozentNr (partiell)
-- (MatrikelNr, VorlesungNr) → Note (vollständig)
Implementation in 2NF
-- Aufteilung in 2NF
CREATE TABLE Studenten_2NF (
MatrikelNr INT PRIMARY KEY,
Name VARCHAR(50),
Semester INT
);
CREATE TABLE Vorlesungen_2NF (
VorlesungNr INT PRIMARY KEY,
Name VARCHAR(50),
DozentNr INT
);
CREATE TABLE Belegungen_2NF (
MatrikelNr INT,
VorlesungNr INT,
Note DECIMAL(3,1),
PRIMARY KEY (MatrikelNr, VorlesungNr),
FOREIGN KEY (MatrikelNr) REFERENCES Studenten_2NF(MatrikelNr),
FOREIGN KEY (VorlesungNr) REFERENCES Vorlesungen_2NF(VorlesungNr)
);
-- Beispieldaten
INSERT INTO Studenten_2NF VALUES (101, 'Max Mustermann', 5);
INSERT INTO Vorlesungen_2NF VALUES (301, 'Datenbanken', 501);
INSERT INTO Belegungen_2NF VALUES (101, 301, 1.3);
Checking for 2NF
-- Test auf partielle Abhängigkeiten
-- Für jedes Nicht-Schlüsselattribut prüfen:
-- Ist es vom gesamten Schlüssel abhängig?
-- Beispiel: StudentName
-- Abhängig von MatrikelNr? Ja
-- Abhängig von VorlesungNr? Nein
-- → Partielle Abhängigkeit → Nicht in 2NF
-- Beispiel: Note
-- Abhängig von MatrikelNr? Nein
-- Abhängig von VorlesungNr? Nein
-- Abhängig von (MatrikelNr, VorlesungNr)? Ja
-- → Volle Abhängigkeit → In 2NF
Third Normal Form (3NF)
Definition and Rules
A relation is in 3NF if:
- It is in 2NF
- No transitive dependencies of non-key attributes exist
Problem: Transitive Dependencies
-- Problem: Not in 3NF (transitive dependencies)
CREATE TABLE Dozenten_Problem (
DozentenNr INT PRIMARY KEY,
Name VARCHAR(50),
Fachbereich VARCHAR(50),
FachbereichLeiter VARCHAR(50)
);
-- Transitive dependencies:
-- DozentenNr → Fachbereich
-- Fachbereich → FachbereichLeiter
-- DozentenNr → FachbereichLeiter (transitive)
Implementation in 3NF
-- Split into 3NF
CREATE TABLE Dozenten_3NF (
DozentenNr INT PRIMARY KEY,
Name VARCHAR(50),
Fachbereich VARCHAR(50)
);
CREATE TABLE Fachbereiche_3NF (
Fachbereich VARCHAR(50) PRIMARY KEY,
Leiter VARCHAR(50)
);
-- Sample data
INSERT INTO Dozenten_3NF VALUES (501, 'Prof. Schmidt', 'Informatik');
INSERT INTO Fachbereiche_3NF VALUES ('Informatik', 'Prof. Dr. Meier');
Complex 3NF Example
-- Complete 3NF schema for university
CREATE TABLE Studenten (
MatrikelNr INT PRIMARY KEY,
Name VARCHAR(50) NOT NULL,
Geburtsdatum DATE,
Fachbereich VARCHAR(50)
);
CREATE TABLE Fachbereiche (
Fachbereich VARCHAR(50) PRIMARY KEY,
Dekan VARCHAR(50),
Gebaeude VARCHAR(20)
);
CREATE TABLE Dozenten (
DozentenNr INT PRIMARY KEY,
Name VARCHAR(50) NOT NULL,
Fachbereich VARCHAR(50),
FOREIGN KEY (Fachbereich) REFERENCES Fachbereiche(Fachbereich)
);
CREATE TABLE Vorlesungen (
VorlesungNr INT PRIMARY KEY,
Titel VARCHAR(100) NOT NULL,
DozentenNr INT,
Fachbereich VARCHAR(50),
FOREIGN KEY (DozentenNr) REFERENCES Dozenten(DozentenNr),
FOREIGN KEY (Fachbereich) REFERENCES Fachbereiche(Fachbereich)
);
CREATE TABLE Belegungen (
MatrikelNr INT,
VorlesungNr INT,
Semester INT,
Note DECIMAL(3,1),
PRIMARY KEY (MatrikelNr, VorlesungNr, Semester),
FOREIGN KEY (MatrikelNr) REFERENCES Studenten(MatrikelNr),
FOREIGN KEY (VorlesungNr) REFERENCES Vorlesungen(VorlesungNr)
);
Boyce-Codd Normal Form (BCNF)
Definition and Rules
A relation is in BCNF if:
- It is in 3NF
- For every functional dependency X → Y: X is a superkey
Problem: BCNF Violation
-- Problem: Not in BCNF
CREATE TABLE Lehrveranstaltungen (
DozentenNr INT,
Fachbereich VARCHAR(50),
VorlesungNr INT,
PRIMARY KEY (DozentenNr, Fachbereich)
);
-- Data:
-- 501, 'Informatik', 301
-- 501, 'Mathematik', 401
-- 502, 'Informatik', 302
-- Functional dependencies:
-- (DozentenNr, Fachbereich) → VorlesungNr (primary key)
-- DozentenNr → Fachbereich (each instructor belongs to exactly one department)
-- Problem: DozentenNr → Fachbereich
-- DozentenNr is not a superkey → BCNF violation!
Implementation in BCNF
-- Split into BCNF
CREATE TABLE Dozenten_Fachbereich (
DozentenNr INT PRIMARY KEY,
Fachbereich VARCHAR(50)
);
CREATE TABLE Fachbereich_Vorlesungen (
Fachbereich VARCHAR(50),
DozentenNr INT,
VorlesungNr INT,
PRIMARY KEY (Fachbereich, DozentenNr)
);
-- Alternative BCNF solution
CREATE TABLE Dozenten (
DozentenNr INT PRIMARY KEY,
Fachbereich VARCHAR(50)
);
CREATE TABLE Vorlesungen_Dozenten (
VorlesungNr INT,
DozentenNr INT,
PRIMARY KEY (VorlesungNr, DozentenNr),
FOREIGN KEY (DozentenNr) REFERENCES Dozenten(DozentenNr)
);
BCNF vs 3NF
-- Example where 3NF ≠ BCNF
CREATE TABLE Projektmitarbeiter (
ProjektNr INT,
MitarbeiterNr INT,
Rolle VARCHAR(50),
PRIMARY KEY (ProjektNr, MitarbeiterNr)
);
-- Assumption: Each employee has exactly one role in each project
-- However: An employee can have the same role in different projects
-- Functional dependencies:
-- (ProjektNr, MitarbeiterNr) → Rolle (primary key)
-- MitarbeiterNr → Rolle (each employee has a fixed role)
-- BCNF violation: MitarbeiterNr → Rolle, but MitarbeiterNr is not a superkey
-- BCNF solution:
CREATE TABLE Mitarbeiter_Rolle (
MitarbeiterNr INT PRIMARY KEY,
Rolle VARCHAR(50)
);
CREATE TABLE Projekt_Mitarbeiter (
ProjektNr INT,
MitarbeiterNr INT,
PRIMARY KEY (ProjektNr, MitarbeiterNr),
FOREIGN KEY (MitarbeiterNr) REFERENCES Mitarbeiter_Rolle(MitarbeiterNr)
);
Normalization Process
Step-by-Step Guide
-- Step 0: Initial table (not normalized)
CREATE TABLE Uni_Daten (
MatrikelNr INT,
StudentName VARCHAR(50),
VorlesungNr INT,
VorlesungName VARCHAR(50),
DozentNr INT,
DozentName VARCHAR(50),
Fachbereich VARCHAR(50),
Note DECIMAL(3,1),
Semester INT
);
-- Step 1: 1NF - Atomic values
-- (already atomic in this example)
-- Step 2: 2NF - Eliminate partial dependencies
CREATE TABLE Studenten_2NF (
MatrikelNr INT PRIMARY KEY,
Name VARCHAR(50),
Semester INT
);
CREATE TABLE Vorlesungen_2NF (
VorlesungNr INT PRIMARY KEY,
Name VARCHAR(50),
DozentNr INT,
Fachbereich VARCHAR(50)
);
CREATE TABLE Noten_2NF (
MatrikelNr INT,
VorlesungNr INT,
Note DECIMAL(3,1),
PRIMARY KEY (MatrikelNr, VorlesungNr)
);
-- Step 3: 3NF - Eliminate transitive dependencies
CREATE TABLE Dozenten_3NF (
DozentenNr INT PRIMARY KEY,
Name VARCHAR(50),
Fachbereich VARCHAR(50)
);
CREATE TABLE Fachbereiche_3NF (
Fachbereich VARCHAR(50) PRIMARY KEY,
-- Additional department information
);
-- Final 3NF structure
CREATE TABLE Studenten (
MatrikelNr INT PRIMARY KEY,
Name VARCHAR(50),
Semester INT
);
CREATE TABLE Fachbereiche (
Fachbereich VARCHAR(50) PRIMARY KEY
);
CREATE TABLE Dozenten (
DozentenNr INT PRIMARY KEY,
Name VARCHAR(50),
Fachbereich VARCHAR(50),
FOREIGN KEY (Fachbereich) REFERENCES Fachbereiche(Fachbereich)
);
CREATE TABLE Vorlesungen (
VorlesungNr INT PRIMARY KEY,
Name VARCHAR(50),
DozentenNr INT,
Fachbereich VARCHAR(50),
FOREIGN KEY (DozentenNr) REFERENCES Dozenten(DozentenNr),
FOREIGN KEY (Fachbereich) REFERENCES Fachbereiche(Fachbereich)
);
CREATE TABLE Belegungen (
MatrikelNr INT,
VorlesungNr INT,
Note DECIMAL(3,1),
PRIMARY KEY (MatrikelNr, VorlesungNr),
FOREIGN KEY (MatrikelNr) REFERENCES Studenten(MatrikelNr),
FOREIGN KEY (VorlesungNr) REFERENCES Vorlesungen(VorlesungNr)
);
Normalization Algorithms
Synthesis Algorithm
-- Analyze functional dependencies
-- F = {MatrikelNr → Name, VorlesungNr → Titel, DozentNr → Name,
-- (MatrikelNr, VorlesungNr) → Note}
-- Step 1: Find minimal cover
-- F_min = {MatrikelNr → Name, VorlesungNr → Titel,
-- DozentNr → Name, (MatrikelNr, VorlesungNr) → Note}
-- Step 2: Group by left-hand side
-- Group 1: MatrikelNr → Name → Relation Studenten(MatrikelNr, Name)
-- Group 2: VorlesungNr → Titel → Relation Vorlesungen(VorlesungNr, Titel)
-- Group 3: DozentNr → Name → Relation Dozenten(DozentenNr, Name)
-- Group 4: (MatrikelNr, VorlesungNr) → Note → Relation Noten(MatrikelNr, VorlesungNr, Note)
-- Step 3: Determine candidate keys and supplement
-- Candidate key: (MatrikelNr, VorlesungNr) for Noten
-- Other relations have their own primary keys
Decomposition Algorithm
-- Initial table with anomalies
CREATE TABLE Probleme (
A INT,
B INT,
C INT,
D INT,
PRIMARY KEY (A, B)
);
-- Functional dependencies: A → C, B → D
-- Step 1: Check for 2NF
-- C depends only on A (partial) → split
CREATE TABLE R1 (A INT PRIMARY KEY, C INT);
CREATE TABLE R2 (A INT, B INT, D INT, PRIMARY KEY (A, B));
-- Step 2: Check for 3NF
-- In R2: B → D (transitive via (A,B) → B) → split
CREATE TABLE R3 (B INT PRIMARY KEY, D INT);
CREATE TABLE R4 (A INT, B INT, PRIMARY KEY (A, B));
-- Final 3NF structure
-- R1(A, C), R3(B, D), R4(A, B)
Practical Examples
E-Commerce Database
-- Normalisiertes E-Commerce Schema
CREATE TABLE Kunden (
KundenNr INT PRIMARY KEY,
Name VARCHAR(50),
Email VARCHAR(100) UNIQUE,
Adresse VARCHAR(200),
Stadt VARCHAR(50),
PLZ VARCHAR(10)
);
CREATE TABLE Kategorien (
KategorieNr INT PRIMARY KEY,
Name VARCHAR(50),
Beschreibung TEXT
);
CREATE TABLE Produkte (
ProduktNr INT PRIMARY KEY,
Name VARCHAR(100),
Preis DECIMAL(10,2),
Beschreibung TEXT,
KategorieNr INT,
Lagerbestand INT,
FOREIGN KEY (KategorieNr) REFERENCES Kategorien(KategorieNr)
);
CREATE TABLE Bestellungen (
BestellNr INT PRIMARY KEY,
KundenNr INT,
Bestelldatum DATE,
Gesamtsumme DECIMAL(10,2),
Status VARCHAR(20),
FOREIGN KEY (KundenNr) REFERENCES Kunden(KundenNr)
);
CREATE TABLE Bestellpositionen (
BestellNr INT,
ProduktNr INT,
Menge INT,
Einzelpreis DECIMAL(10,2),
PRIMARY KEY (BestellNr, ProduktNr),
FOREIGN KEY (BestellNr) REFERENCES Bestellungen(BestellNr),
FOREIGN KEY (ProduktNr) REFERENCES Produkte(ProduktNr)
);
Library System
-- Normalisiertes Bibliotheks-Schema
CREATE TABLE Autoren (
AutorNr INT PRIMARY KEY,
Name VARCHAR(50),
Geburtsjahr INT
);
CREATE TABLE Buecher (
BuchNr INT PRIMARY KEY,
Titel VARCHAR(100),
ISBN VARCHAR(20) UNIQUE,
Erscheinungsjahr INT,
Verlag VARCHAR(50)
);
CREATE TABLE Buch_Autoren (
BuchNr INT,
AutorNr INT,
PRIMARY KEY (BuchNr, AutorNr),
FOREIGN KEY (BuchNr) REFERENCES Buecher(BuchNr),
FOREIGN KEY (AutorNr) REFERENCES Autoren(AutorNr)
);
CREATE TABLE Leser (
Lesernummer INT PRIMARY KEY,
Name VARCHAR(50),
Adresse VARCHAR(200),
Telefon VARCHAR(20)
);
CREATE TABLE Exemplare (
ExemplarNr INT PRIMARY KEY,
BuchNr INT,
Status VARCHAR(20),
FOREIGN KEY (BuchNr) REFERENCES Buecher(BuchNr)
);
CREATE TABLE Ausleihen (
AusleihNr INT PRIMARY KEY,
Lesernummer INT,
ExemplarNr INT,
Ausleihdatum DATE,
Rueckgabedatum DATE,
FOREIGN KEY (Lesernummer) REFERENCES Leser(Lesernummer),
FOREIGN KEY (ExemplarNr) REFERENCES Exemplare(ExemplarNr)
);
Denormalization
When and why denormalize?
-- Performance-optimiertes Schema (denormalisiert)
CREATE TABLE Produkt_Statistiken (
ProduktNr INT PRIMARY KEY,
Name VARCHAR(100),
Preis DECIMAL(10,2),
KategorieName VARCHAR(50), -- Denormalisiert
KategorieBeschreibung TEXT, -- Denormalisiert
GesamtVerkaufsmenge INT, -- Denormalisiert (Aggregat)
LetzteBestellung DATE -- Denormalisiert (Aggregat)
);
-- Advantages:
-- Fewer JOINs required
-- Faster queries
-- Better read performance
-- Disadvantages:
-- Redundancy
-- Update anomalies
-- More storage needed
Denormalization strategies
-- 1. Vorberechnete Aggregate
CREATE TABLE Monatsverkaufe (
Monat DATE,
ProduktNr INT,
Verkaufsmenge INT,
Umsatz DECIMAL(12,2),
PRIMARY KEY (Monat, ProduktNr)
);
-- 2. Copied attributes
CREATE TABLE Bestellungen_Kurz (
BestellNr INT PRIMARY KEY,
KundenName VARCHAR(50), -- Copied from Kunden
KundenEmail VARCHAR(100), -- Copied from Kunden
Bestelldatum DATE,
Gesamtsumme DECIMAL(10,2)
);
-- 3. Hierarchical data
CREATE TABLE Mitarbeiter_Hierarchie (
MitarbeiterNr INT PRIMARY KEY,
Name VARCHAR(50),
VorgesetzterNr INT,
Pfad VARCHAR(200), -- Denormalisierter Pfad
Ebene INT -- Denormalisierte Tiefe
);
Exam-Relevant Concepts
Important Definitions
- Functional Dependency: X → Y means Y depends on X
- Full Dependency: Y depends on the entire X
- Partial Dependency: Y depends on part of X
- Transitive Dependency: X → Y and Y → Z ⇒ X → Z
Normal Forms Overview
| Normal Form | Main Problem | Solution |
|---|---|---|
| 1NF | Repeating groups | Atomic values |
| 2NF | Partial dependencies | Division by keys |
| 3NF | Transitive dependencies | Elimination of transitivity |
| BCNF | Non-key determinants | Every determinant is superkey |
Typical Exam Tasks
- Identify functional dependencies
- Normalize given relations
- Explain anomalies and their causes
- Compare normal forms
- Decide on denormalization
Summary
Normalization is fundamental for high-quality database designs:
- 1NF: Atomic values and unique rows
- 2NF: Eliminates partial dependencies
- 3NF: Eliminates transitive dependencies
- BCNF: Strongest normal form for practical application
The right balance between normalization and performance is crucial for successful database systems.
Recommended Reading: Databases
Keine Bücher für Kategorie "datenbanken" gefunden.