Eén tabel, drie problemen bij hardloopclub De Zuidlopers
Hardloopclub De Zuidlopers heeft 340 leden en werkt met één tabel Inschrijving in een relationele database. De kolommen zijn Lidnummer, Naam, Geboortedatum, TrainerNaam, TrainerEmail, Trainingsdag, Wedstrijd1, Wedstrijd2 en Wedstrijd3. Elk lid zit in precies één trainingsgroep en heeft precies één trainer. Toen trainer Youssef zijn e-mailadres wijzigde, moest de beheerder 46 rijen aanpassen; daarbij ging het mis en kregen twaalf leden de trainingstijden van een andere groep. Nieuwe leden die nog geen wedstrijd liepen, hebben in Wedstrijd1 tot en met Wedstrijd3 de waarde NULL. De club wil maximaal drie resultaten per lid vastleggen en verwacht niet dat dit aantal groeit. De wedstrijdcommissie wil per trainer kunnen opvragen hoeveel leden er in de groep zitten. De beheerder stelt voor niets aan de structuur te veranderen en alleen een extra kolom TrainerEmail2 toe te voegen, zodat een oude en een nieuwe adres naast elkaar staan.
1. Leg uit welke twee soorten anomalieën in deze tabel zitten en geef bij elk een concreet voorbeeld uit de situatie van De Zuidlopers.
Read the worked answer
1. Update-anomalie. De gegevens van een trainer staan in elke rij van zijn groep opnieuw opgeslagen: de 46 leden van Youssef hebben allemaal dezelfde TrainerNaam en TrainerEmail. Bij een wijziging moeten al die 46 rijen aangepast worden. Gebeurt dat niet overal, dan ontstaan tegenstrijdige gegevens. Dat is precies wat er misging: na de e-mailwijziging kregen twaalf leden gegevens van een andere groep, omdat de rijen niet meer bij elkaar hoorden. De gegevens zijn redundant en daardoor inconsistent te maken.
2. Insert-anomalie. Je kunt geen trainer vastleggen die nog geen leden heeft: zonder lid is er geen rij en dus geen plek voor TrainerNaam en TrainerEmail. Omgekeerd kun je geen lid toevoegen zonder trainer, terwijl een nieuw lid zich juist eerst aanmeldt en pas later bij een groep komt.
De NULL-waarden in Wedstrijd1–3 zijn op zich geen anomalie, maar het gevolg van de keuze om herhalende kolommen te gebruiken.
2. Ontwerp een opsplitsing in drie tabellen. Geef per tabel de kolommen, de primaire sleutel en de foreign keys, en leg uit waarom de NULL-waarden in je nieuwe ontwerp verdwijnen.
Read the worked answer
Trainer(TrainerId PK, TrainerNaam, TrainerEmail, Trainingsdag) Lid(Lidnummer PK, Naam, Geboortedatum, TrainerId FK → Trainer) Uitslag(Lidnummer FK → Lid, WedstrijdNummer, Tijd, PK = (Lidnummer, WedstrijdNummer))
Redenering. Het e-mailadres van een trainer staat nu één keer in Trainer; een wijziging is één rij en kan niet meer half misgaan. Een trainer zonder leden is één rij in Trainer, dus de insert-anomalie is weg. Een lid zonder uitslagen heeft simpelweg geen rijen in Uitslag: er is geen kolom die leeg moet blijven, dus er ontstaan geen NULL-waarden meer. De combinatie (Lidnummer, WedstrijdNummer) is uniek en werkt als primaire sleutel; hetzelfde wedstrijdnummer kan bij verschillende leden voorkomen. Het aantal leden per trainer vraag je op met SELECT TrainerId, COUNT(*) FROM Lid GROUP BY TrainerId en dat koppel je aan Trainer voor de naam. Let op de trade-off: de grens van maximaal drie resultaten zit nu in de programmatuur en niet meer in de tabelstructuur.
3. Beoordeel het voorstel van de beheerder om niets te veranderen en alleen TrainerEmail2 toe te voegen. Betrek in je antwoord ook de keuze om drie vaste wedstrijdkolommen te houden.
Read the worked answer
Het voorstel verdubbelt precies het probleem dat het wil oplossen: de trainercontactgegevens staan dan in twee kolommen op 46 rijen, dus 92 cellen die hetzelfde horen te zeggen. De vraag 'welke is nu geldig?' wordt niet door de database beantwoord maar door degene die toevallig de laatste update deed. Ook de fout met de verwisselde groepen wordt er niet door hersteld, want die kwam uit de redundante koppeling tussen lid en trainer. Afwijzen dus.
De drie vaste wedstrijdkolommen zijn een ander verhaal. Hier is het aantal echt begrensd (de commissie legt maximaal drie resultaten vast en verwacht geen groei), dus je ruilt flexibiliteit bewust in voor eenvoud: geen extra tabel, geen join bij de meest gebruikte opvraag. Dat is verdedigbaar zolang de grens hard is en gedocumenteerd staat. Wordt er ooit een vierde wedstrijd toegestaan, dan moet de hele tabel gemigreerd worden. Vuistregel: vaste herhalende kolommen mogen alleen als het maximum zeker is; de trainerrelatie voldoet niet aan die eis, het wedstrijdaantal wel.