3  Normaliseren

Belangrijk

Je leest de versie die nog in ontwikkeling is. Dit hoofdstuk is nog een concept; er kan nog veel veranderen.

IntroLeerdoelen

Na het bestuderen van dit hoofdstuk kun je:

  • uitleggen wat een functionele afhankelijkheid is en die herkennen in een verzameling gegevens;
  • de sleutel van een tabel bepalen, ook als die uit meerdere kolommen bestaat;
  • een document of werkblad omzetten naar de ongenormaliseerde vorm;
  • een tabelstructuur stap voor stap brengen naar de eerste, tweede en derde normaalvorm;
  • een ontwerp toetsen op de derde normaalvorm, ook als dat ontwerp met een ERD is gemaakt.

In Hoofdstuk 2 heb je een ontwerp gemaakt door te beginnen bij een beschrijving van de werkelijkheid. Bij normaliseren werk je de andere kant op: je begint bij de gegevens zelf. Dat kan een Excel-werkblad zijn, een formulier, een factuur of een overzicht uit een bestaand systeem. Die gegevens breng je stap voor stap terug tot een verzameling tabellen zonder herhaling en zonder anomalieën.

Bij elke stap toets je de tabellen aan een vaste regel, een normaalvorm. We behandelen er drie: de eerste, tweede en derde normaalvorm, afgekort als 1NV, 2NV en 3NV. Een ontwerp in de derde normaalvorm is in de praktijk vrijwel altijd goed genoeg.

3.1 Functionele afhankelijkheid

Normaliseren draait om één begrip: de functionele afhankelijkheid.

Functionele afhankelijkheid

Attribuut B is functioneel afhankelijk van attribuut A als bij elke waarde van A precies één waarde van B hoort. We noteren dit als A → B en zeggen: A bepaalt B. A heet de determinant.

Een paar voorbeelden uit de webshop:

  • KlantNr → Naam. Bij klantnummer K01 hoort altijd dezelfde naam, Jansen.
  • ProductNr → Omschrijving, Prijs. Bij product P10 hoort altijd dezelfde omschrijving en prijs.
  • OrderNr → Datum, KlantNr. Een order heeft één datum en is geplaatst door één klant.
  • (OrderNr, ProductNr) → Aantal. Het aantal hoort bij de combinatie van een order en een product. Alleen het ordernummer is niet genoeg (een order bevat meerdere producten), alleen het productnummer ook niet (een product komt in meerdere orders voor).

Andersom geldt Naam → KlantNr niet: er kunnen meerdere klanten Jansen heten.

BelangrijkAfhankelijkheden volgen uit de bedrijfsregels

Of een functionele afhankelijkheid bestaat, kun je niet met zekerheid afleiden uit een paar rijen voorbeeldgegevens. Dat er in een tabel toevallig geen twee klanten met dezelfde naam staan, betekent niet dat Naam → KlantNr. Afhankelijkheden volgen uit de regels van de organisatie: wat kan er in werkelijkheid voorkomen? Twijfel je, vraag het dan na bij de opdrachtgever.

3.1.1 Sleutels

Met functionele afhankelijkheden kun je ook de sleutel van een tabel precies omschrijven. Een sleutel is een attribuut, of een combinatie van attributen, die alle andere attributen van de tabel bepaalt, en waarvan je geen enkel attribuut kunt weglaten zonder dat die eigenschap verloren gaat. Een sleutel die uit meer dan één attribuut bestaat, heet een samengestelde sleutel.

3.2 Stap 0: van document naar ongenormaliseerde vorm

Normaliseren begint met een bron. Neem de orderbevestiging die de webshop naar klant Jansen stuurt.

Orderbevestiging

Ordernummer: 1001
Datum: 2 maart 2026
Klantnummer: K01
Naam: Jansen
Woonplaats: Utrecht

ProductNr Omschrijving Prijs Aantal Subtotaal
P10 Koffiebonen 1kg 14,95 2 29,90
P12 Filterpapier 3,49 1 3,49

Totaal: 33,39

Uit dit document stel je eerst een lijst op van alle attributen. Daarbij let je op drie dingen.

  1. Laat afgeleide gegevens weg. Subtotaal en Totaal kun je altijd uitrekenen uit Prijs en Aantal. Ze horen niet in de database.
  2. Kies duidelijke, unieke namen. Twee attributen die allebei Nummer heten, worden OrderNr en KlantNr.
  3. Geef herhalende groepen aan. Op één orderbevestiging staan meerdere productregels. Dat deel herhaalt zich binnen één document; die worden tussen accolades gezet.
OpmerkingNotatie herhalende groep

Er bestaan verschillende gewoontes om een herhalende groep weer te geven, zoals accolades {}, blokhaken [], met een sterretje, …

De meest gebruikte notatie is die met accolades en deze wordt in dit leerboek gebruikt.

Daarna bepaal je de sleutel van het geheel. Elk document heeft een uniek ordernummer, dus OrderNr is de sleutel. Het resultaat heet de ongenormaliseerde vorm (0NV):

  • Order(OrderNr, Datum, KlantNr, Naam, Woonplaats, {ProductNr, Omschrijving, Prijs, Aantal})

3.3 Stap 1: de eerste normaalvorm

Eerste normaalvorm (1NV)

Een tabel is in de eerste normaalvorm als elk attribuut één enkele waarde bevat en er geen herhalende groepen zijn.

Een herhalende groep los je op door haar af te splitsen naar een eigen tabel. Die nieuwe tabel krijgt de sleutel van de oorspronkelijke tabel mee, anders weet je niet bij welke order een regel hoort. Vervolgens bepaal je de sleutel van de nieuwe tabel: welk attribuut bepaalt binnen één order welke regel het is? Dat is ProductNr. De sleutel wordt daarmee de combinatie (OrderNr, ProductNr).

  • Order(OrderNr, Datum, KlantNr, Naam, Woonplaats)
  • OrderRegel(OrderNr, ProductNr, Omschrijving, Prijs, Aantal)
TipDe platte tabel is ook 1NV

De tabel uit Tabel 1.1 in Hoofdstuk 1 is ook in de eerste normaalvorm: elke cel bevat één waarde en er zijn geen herhalende groepen. De sleutel van die tabel is (OrderNr, ProductNr). Toch zag je daar alle anomalieën. De eerste normaalvorm alleen is dus niet genoeg; daarvoor zijn de volgende stappen nodig.

3.4 Stap 2: de tweede normaalvorm

Tweede normaalvorm (2NV)

Een tabel is in de tweede normaalvorm als zij in de eerste normaalvorm is en elk attribuut dat geen deel van de sleutel is, afhangt van de hele sleutel en niet van een deel ervan.

Een afhankelijkheid van een deel van de sleutel heet een partiële afhankelijkheid. Die kan alleen voorkomen bij een samengestelde sleutel. Een tabel in 1NV met een sleutel van één attribuut is daarom automatisch ook in 2NV.

Controleer de tabellen uit stap 1:

  • Order heeft een sleutel van één attribuut en is dus in 2NV.
  • OrderRegel heeft de samengestelde sleutel (OrderNr, ProductNr). Bekijk elk overig attribuut:
    • Aantal hangt af van de hele sleutel: (OrderNr, ProductNr) → Aantal. In orde.
    • Omschrijving en Prijs hangen alleen af van ProductNr: ProductNr → Omschrijving, Prijs. Dat is een partiële afhankelijkheid.

Een partiële afhankelijkheid los je op door het deel van de sleutel, met de attributen die ervan afhangen, af te splitsen naar een nieuwe tabel. Het sleuteldeel blijft ook in de oorspronkelijke tabel staan, nu als vreemde sleutel.

  • Order(OrderNr, Datum, KlantNr, Naam, Woonplaats)
  • OrderRegel(OrderNr, ProductNr, Aantal)
  • Product(ProductNr, Omschrijving, Prijs)

We zijn er hierboven van uitgegaan dat de prijs alleen van het product afhangt. Dat klopt zolang prijzen nooit veranderen. Past de webshop de prijzen wel eens aan, dan is de prijs op de orderbevestiging de prijs op het moment van bestellen. Die hangt af van de combinatie van order en product en blijft als Verkoopprijs in OrderRegel staan. De actuele prijs in Product komt dan uit een andere bron, zoals de prijslijst. Hier zie je opnieuw dat functionele afhankelijkheden uit de bedrijfsregels volgen. In Hoofdstuk 2 kwam je dezelfde keuze tegen.

3.5 Stap 3: de derde normaalvorm

Derde normaalvorm (3NV)

Een tabel is in de derde normaalvorm als zij in de tweede normaalvorm is en geen attribuut dat geen deel van de sleutel is, afhangt van een ander attribuut dat ook geen deel van de sleutel is.

Een afhankelijkheid via een tussenliggend attribuut heet een transitieve afhankelijkheid. In de tabel [Order]{..entity} geldt:

  • OrderNr → KlantNr, en
  • KlantNr → Naam, Woonplaats.

Naam en Woonplaats hangen dus niet rechtstreeks van de sleutel OrderNr af, maar via KlantNr. Dat is precies de oorzaak van de wijzigingsanomalie uit Hoofdstuk 1: de woonplaats van een klant staat bij elke order opnieuw.

Een transitieve afhankelijkheid los je op door de determinant (KlantNr) met de attributen die ervan afhangen af te splitsen naar een nieuwe tabel. De determinant blijft in de oorspronkelijke tabel staan als vreemde sleutel.

  • Klant(KlantNr, Naam, Woonplaats)
  • Order(OrderNr, Datum, KlantNr)
  • OrderRegel(OrderNr, ProductNr, Aantal)
  • Product(ProductNr, Omschrijving, Prijs)

Controleer ook de andere tabellen. In OrderRegel en Product is er geen attribuut buiten de sleutel dat een ander attribuut bepaalt. Alle vier tabellen zijn nu in de derde normaalvorm.

TipEzelsbruggetje

In een tabel in de derde normaalvorm hangt elk attribuut af van de sleutel (1NV), de hele sleutel (2NV) en niets dan de sleutel (3NV).

3.6 Het resultaat

Het genormaliseerde ontwerp is hetzelfde als het ontwerp dat je in Hoofdstuk 2 met een ERD hebt gemaakt. Je kunt het dus ook als ERD weergeven (Figuur 3.1). De relaties lees je af aan de vreemde sleutels: een vreemde sleutel staat altijd aan de veel-kant van een relatie.

erDiagram
    Klant ||--o{ Order : plaatst
    Order ||--|{ OrderRegel : bevat
    Product ||--o{ OrderRegel : "komt voor in"
    Klant {
        string KlantNr PK
        string Naam
        string Woonplaats
    }
    Order {
        int OrderNr PK
        date Datum
        string KlantNr FK
    }
    OrderRegel {
        int OrderNr PK, FK
        string ProductNr PK, FK
        int Aantal
    }
    Product {
        string ProductNr PK
        string Omschrijving
        decimal Prijs
    }
Figuur 3.1: Het resultaat van de normalisatie, weergegeven als ERD

Controleer ten slotte of de anomalieën uit Hoofdstuk 1 zijn verdwenen:

  • De woonplaats van een klant staat op één plek. Een verhuizing is één wijziging. Geen wijzigingsanomalie.
  • Een nieuw product kun je opnemen in Product, ook als niemand het heeft besteld. Geen invoeganomalie.
  • Als je een order verwijdert, blijven de gegevens van de klant in Klant staan. Geen verwijderanomalie.
OpmerkingHogere normaalvormen

Naast de eerste drie normaalvormen bestaan er nog strengere vormen, zoals de Boyce-Codd-normaalvorm (BCNV) en de vierde en vijfde normaalvorm. Die lossen problemen op die alleen in bijzondere situaties voorkomen, bijvoorbeeld bij tabellen met meerdere overlappende samengestelde sleutels. Voor de meeste praktische ontwerpen is de derde normaalvorm voldoende, en dat is ook het niveau dat in dit boek wordt gevraagd.

3.7 Het stappenplan samengevat

  1. Ongenormaliseerde vorm: noteer alle attributen uit de bron, laat afgeleide gegevens weg, geef herhalende groepen aan en bepaal de sleutel.
  2. Eerste normaalvorm: splits elke herhalende groep af naar een eigen tabel, neem de sleutel van de oorspronkelijke tabel mee en bepaal de sleutel van de nieuwe tabel.
  3. Tweede normaalvorm: zoek bij elke samengestelde sleutel naar attributen die van een deel van de sleutel afhangen, en splits die met dat sleuteldeel af.
  4. Derde normaalvorm: zoek naar attributen die afhangen van een ander attribuut buiten de sleutel, en splits die met hun determinant af.
  5. Samenvoegen en controleren: voeg tabellen met dezelfde sleutel samen. Dat kan nodig zijn als je meerdere documenten hebt genormaliseerd, zoals een orderbevestiging en een klantenoverzicht. Controleer daarna of de anomalieën verdwenen zijn.
TipNormaliseren als controle op een ERD

Ook als je een ontwerp met een ERD hebt gemaakt, is normaliseren nuttig. Zet het ERD om naar tabellen en loop elke tabel na: hangt elk attribuut af van de sleutel, de hele sleutel en niets dan de sleutel? Zo vind je fouten die in de tekening niet opvallen, zoals een attribuut dat bij de verkeerde entiteit staat.

WaarschuwingVeelgemaakte fouten

3.8 Veelgemaakte fouten

  • Afgeleide gegevens, zoals subtotalen en totalen, laten staan.
  • Bij het afsplitsen van een herhalende groep de sleutel van de oorspronkelijke tabel vergeten mee te nemen.
  • Na het afsplitsen bij stap 2 of 3 de determinant ook uit de oorspronkelijke tabel verwijderen. Die moet daar blijven als vreemde sleutel, anders gaat de relatie verloren.
  • Functionele afhankelijkheden afleiden uit toevallige voorbeeldgegevens in plaats van uit de bedrijfsregels.
  • Denken dat een tabel met een sleutel van één attribuut nog problemen met de tweede normaalvorm kan hebben. Die tabel is automatisch in 2NV; kijk direct naar de derde normaalvorm.

3.9 Oefeningen

3.9.1 Oefening 1: functionele afhankelijkheden

Een bibliotheek houdt de uitleningen bij in de volgende tabel. Een lid kan hetzelfde boek op verschillende dagen lenen.

Tabel 3.1: Uitleningen van een bibliotheek
LidNr Lidnaam ISBN Titel Uitleendatum Inleverdatum
12 Bakker 9789012345678 Databases in de praktijk 2026-01-10 2026-01-31
12 Bakker 9789087654321 Excel voor economen 2026-01-10 2026-01-24
15 Smit 9789012345678 Databases in de praktijk 2026-02-03 2026-02-20
  1. Noem alle functionele afhankelijkheden.
  2. Wat is de sleutel van deze tabel?
  1. LidNr → Lidnaam
    ISBN → Titel
    (LidNr, ISBN, Uitleendatum) → Inleverdatum
  2. De sleutel is (LidNr, ISBN, Uitleendatum). LidNr en ISBN samen zijn niet genoeg, omdat een lid hetzelfde boek meer dan eens kan lenen.

Normaliseren levert op: Lid(LidNr, Lidnaam), Boek(ISBN, Titel) en Uitlening(LidNr, ISBN, Uitleendatum, Inleverdatum).

3.9.2 Oefening 2: in welke normaalvorm?

Bepaal voor elke tabel in welke normaalvorm zij is (0NV, 1NV, 2NV of 3NV). Licht je antwoord toe.

  1. Medewerker(MedewerkerNr, Naam, AfdelingNr, Afdelingsnaam)
  2. Inschrijving(StudentNr, VakCode, Inschrijfdatum, Vaknaam)
  3. Product(ProductNr, Omschrijving, Prijs)
  4. Klant(KlantNr, Naam, Telefoonnummers), waarbij in Telefoonnummers waarden staan als “06-12345678, 020-7654321”
  1. 2NV. De sleutel bestaat uit één attribuut, dus de tabel is automatisch in 2NV. Er is een transitieve afhankelijkheid: AfdelingNr → Afdelingsnaam.
    Oplossing: Afdeling(AfdelingNr, Afdelingsnaam) afsplitsen.

  2. 1NV. Vaknaam hangt alleen af van VakCode, een deel van de sleutel.
    Oplossing: Vak(VakCode, Vaknaam) afsplitsen.

  3. 3NV. Omschrijving en Prijs hangen rechtstreeks en alleen van ProductNr af.

  4. 0NV. Telefoonnummers bevat meerdere waarden in één attribuut.
    Oplossing: een aparte tabel Telefoon(KlantNr, Telefoonnummer).

3.9.3 Oefening 3: resultatenoverzicht

Een opleiding houdt de resultaten van studenten bij in een werkblad (Tabel 3.2). Elk vak heeft één docent als coördinator. Normaliseer dit werkblad tot de derde normaalvorm en teken het resultaat als ERD.

Tabel 3.2: Resultaten van studenten
StudentNr Naam Opleiding VakCode Vaknaam EC DocentCode Docentnaam Cijfer
2401 Yilmaz BE STA1 Statistiek 1 5 HVR Van Rijn 7,5
2401 Yilmaz BE DBS Databases 4 KDB De Boer 8,0
2402 Hendriks ACC STA1 Statistiek 1 5 HVR Van Rijn 6,0
2403 Mulder BE DBS Databases 4 KDB De Boer 5,5

1NV. Het werkblad heeft geen herhalende groepen en elke cel bevat één waarde. De sleutel is (StudentNr, VakCode).

  • Resultaat(StudentNr, VakCode, Naam, Opleiding, Vaknaam, EC, DocentCode, Docentnaam, Cijfer)

2NV. StudentNr → Naam, Opleiding en VakCode → Vaknaam, EC, DocentCode, Docentnaam zijn partiële afhankelijkheden.

  • Student(StudentNr, Naam, Opleiding)
  • Vak(VakCode, Vaknaam, EC, DocentCode, Docentnaam)
  • Resultaat(StudentNr, VakCode, Cijfer)

3NV. In Vak geldt DocentCode → Docentnaam: een transitieve afhankelijkheid.

  • Student(StudentNr, Naam, Opleiding)
  • Docent(DocentCode, Docentnaam)
  • Vak(VakCode, Vaknaam, EC, DocentCode)
  • Resultaat(StudentNr, VakCode, Cijfer)

erDiagram
    Student ||--o{ Resultaat : behaalt
    Vak ||--o{ Resultaat : "wordt behaald in"
    Docent ||--o{ Vak : coördineert
    Student {
        int StudentNr PK
        string Naam
        string Opleiding
    }
    Vak {
        string VakCode PK
        string Vaknaam
        int EC
        string DocentCode FK
    }
    Docent {
        string DocentCode PK
        string Docentnaam
    }
    Resultaat {
        int StudentNr PK, FK
        string VakCode PK, FK
        decimal Cijfer
    }

Opmerking: als een student een vak meerdere keren mag herkansen, is (StudentNr, VakCode) geen geldige sleutel meer. Dan moet bijvoorbeeld een toetsdatum aan de sleutel worden toegevoegd.

3.9.4 Oefening 4: fietsverhuur

Het fietsverhuurbedrijf uit oefening 3 van Hoofdstuk 2 geeft bij elke verhuur een verhuurbon mee.

Verhuurbon

Verhuurnummer: 501
Periode: 10 juni 2026 t/m 12 juni 2026
Klantnummer: 17
Naam: Pieters
Telefoon: 06-12345678

FietsNr Type Dagprijs
3 E-bike 25,00
8 Stadsfiets 12,50

Totaal per dag: 37,50

  1. Normaliseer de verhuurbon tot de derde normaalvorm. Ga ervan uit dat de dagprijs per fiets kan verschillen.
  2. Vergelijk je resultaat met de uitwerking van de ERD-oefening. Zijn de ontwerpen gelijk?
  3. Stel dat alle fietsen van hetzelfde type dezelfde dagprijs hebben. Wat verandert er dan aan je ontwerp?

a. 0NV (Totaal per dag is afgeleid en vervalt):

  • Verhuur(VerhuurNr, Begindatum, Einddatum, KlantNr, Naam, Telefoon, {FietsNr, Type, Dagprijs})

1NV:

  • Verhuur(VerhuurNr, Begindatum, Einddatum, KlantNr, Naam, Telefoon)
  • VerhuurRegel(VerhuurNr, FietsNr, Type, Dagprijs)

2NV: FietsNr → Type, Dagprijs is een partiële afhankelijkheid.

  • Verhuur(VerhuurNr, Begindatum, Einddatum, KlantNr, Naam, Telefoon)
  • VerhuurRegel(VerhuurNr, FietsNr)
  • Fiets(FietsNr, Type, Dagprijs)

3NV: KlantNr → Naam, Telefoon is een transitieve afhankelijkheid.

  • Klant(KlantNr, Naam, Telefoon)
  • Verhuur(VerhuurNr, Begindatum, Einddatum, KlantNr)
  • VerhuurRegel(VerhuurNr, FietsNr)
  • Fiets(FietsNr, Type, Dagprijs)
  1. Het ontwerp is gelijk aan dat van de ERD-oefening, op één punt na. Daar stond de dagprijs ook in VerhuurRegel, om de prijs op het moment van verhuren vast te leggen. Dat is een keuze die afhangt van de bedrijfsregels, niet van de methode.

  2. Dan geldt Type → Dagprijs. In de tabel Fiets is dat een transitieve afhankelijkheid, en die moet worden afgesplitst: FietsType(Type, Dagprijs) en Fiets(FietsNr, Type).