Normaliseren¶
Als je een database ontwerpt, dan wil je dat je zo min mogelijk data opslaat. Je wil geen dubbele data opslaan en je wil geen data opslaan die je niet nodig hebt. Door tabellen te koppelen kan je data opslaan in een tabel en deze data gebruiken in een andere tabel. Dit is een belangrijk onderdeel van het ontwerpen van een database.
Hoe vind je de juiste tabellen om te koppelen?
Vaak is het intuïtief duidelijk welke tabellen je moet koppelen. Neem de onderstaande tabel:
id | naam | adres | postcode | woonplaats |
---|---|---|---|---|
1 | Jan | Kerkstraat 1 | 1234 AB | Amsterdam |
2 | Piet | Kerkstraat 1 | 1234 AB | Amsterdam |
3 | Klaas | Kerkstraat 1 | 1234 AB | Amsterdam |
In deze tabel staan drie personen. De personen hebben allemaal hetzelfde adres. Je kan dit adres opslaan in een aparte tabel en deze tabel koppelen aan de tabel met personen. De tabel met adressen ziet er dan als volgt uit:
id | adres | postcode | woonplaats |
---|---|---|---|
1 | Kerkstraat 1 | 1234 AB | Amsterdam |
De tabel met personen ziet er dan als volgt uit:
id | naam | adres_id |
---|---|---|
1 | Jan | 1 |
2 | Piet | 1 |
3 | Klaas | 1 |
Je hoeft nu niet meer het adres van een persoon op te slaan in de tabel met personen. Je kan het adres van een persoon opzoeken in de tabel met adressen. Zo heb je minder data opgeslagen.
Wanneer de gegevens complexer worden, wordt het minder intuïtief welke tabellen je moet koppelen. Met behulp van normaliseren kan je bepalen welke tabellen je nodig hebt en hoe je ze moet koppelen.
Normaliseren¶
Normaliseren is een proces waarbij je een database ontwerpt door een tabel te splitsen in meerdere tabellen. Je kan een tabel normaliseren door te kijken naar de functionele afhankelijkheden tussen de attributen in de tabel. Een functionele afhankelijkheid is een relatie tussen twee attributen in een tabel. Als je de waarde van het ene attribuut weet, dan kan je de waarde van het andere attribuut bepalen.
Aangeleverde tabel¶
Als voorbeeld nemen we de volgende tabel:
Voorbeeld tabel: Users en Achievements
userId | userName | userAvatarImage | userJoindate | achievementType | achievementText | achiemevementImage | scores |
---|---|---|---|---|---|---|---|
1 | Storm | storm.png | 10/09/1997 | noShoot | Don’t shoort enemies for 1 minute. | noshoot.png | 1342314 behaald op 01-02-2023 , 3245523, behaald op 02-03-2012 |
1 | dodgy | Dodge 10 enemy bullets in one session. | dodgy.png | ||||
1 | richkid | Collect 10E10 coins in one session. | richkid.png | ||||
2 | Juffrouw Jannie | juffrouw_jannie.png | 12/01/2001 | urdead | Die within 2 seconds. | urdead.png | 4351542 behaald op 23-10-1990, 34578 behaald op 23-10-2008 |
2 | noShoot | Don’t shoort enemies for 1 minute. | noshoot.png | ||||
2 | dodgy | Dodge 10 enemy bullets in one session. | dodgy.png | ||||
3 | Edgar | edgar.png | 05/04/2023 | dodgy | Dodge 10 enemy bullets in one session. | dodgy.png | 234 behaald op 23-10-1991 ,536 behaald op 23-10-1992, 654 behaald op 23-10-1993 |
3 | richkid | Collect 10E10 coins in one session. | richkid.png | ||||
3 | urdead | Die within 2 seconds. | urdead.png | ||||
4 | Jos | stift_master.png | 01/02/2019 | noShoot | Don’t shoort enemies for 1 minute. | noshoot.png | 1 behaald op 23-10-2003, 2 behaald op 23-10-2002, 3 behaald op 23-10-2001 |
4 | richkid | Collect 10E10 coins in one session. | richkid.png | ||||
4 | urdead | Die within 2 seconds. | urdead.png |
In deze tabel is getracht om de gegevens (scores en achievements) van een aantal spelers op te slaan.
Een aantal dingen vallen op:
- De tabel bevat veel lege cellen.
- De tabel bevat herhalende gegevens. De achievementType
noShoot
komt meerdere keren voor. - De tabel bevat een lijst met scores. De scores zijn gescheiden door een komma. De tabel bevat dus meerdere waarden in één cel.
- De tabel bevat een lijst met achievements. De eigenschappen van iedere achievement zijn opgeslagen voor elke keer dat een achievement behaald is.
Om deze data beter toegankelijk te maken voor een applicatie, kan je de tabel normaliseren.
1e normaalvorm¶
De regels voor de eerste normaalvorm zijn:
- geen herhalende groepen
- geen kolommen met meerdere waarden
- iedere rij heeft een unieke identificatie
De eerste normaalvorm is een tabel waarin geen herhalende groepen voorkomen. Een herhalende groep is een groep attributen die meerdere waarden bevat. In de tabel hierboven komen twee herhalende groepen voor: de scores en de achievements.
De scores kan je normaliseren door een aparte tabel te maken met de scores. De behaalde datum kan je opslaan in een aparte kolom. De tabel met scores ziet er dan als volgt uit:
userId | score | date |
---|---|---|
1 | 1342314 | 01-02-2023 |
1 | 3245523 | 02-03-2012 |
2 | 4351542 | 23-10-1990 |
2 | 34578 | 23-10-2008 |
3 | 234 | 23-10-1991 |
3 | 536 | 23-10-1992 |
3 | 654 | 23-10-1993 |
4 | 1 | 23-10-2003 |
4 | 2 | 23-10-2002 |
4 | 3 | 23-10-2001 |
De primary key van deze tabel bestaat uit de userId
en de date
.
De tabel met gebruikers ziet er dan als volgt uit:
userId | achievementType | achievementText | achievementImage |
---|---|---|---|
1 | noShoot | Don’t shoort enemies for 1 minute. | noshoot.png |
1 | dodgy | Dodge 10 enemy bullets in one session. | dodgy.png |
1 | richkid | Collect 10E10 coins in one session. | richkid.png |
2 | urdead | Die within 2 seconds. | urdead.png |
2 | noShoot | Don’t shoort enemies for 1 minute. | noshoot.png |
2 | dodgy | Dodge 10 enemy bullets in one session. | dodgy.png |
3 | dodgy | Dodge 10 enemy bullets in one session. | dodgy.png |
3 | richkid | Collect 10E10 coins in one session. | richkid.png |
3 | urdead | Die within 2 seconds. | urdead.png |
4 | noShoot | Don’t shoort enemies for 1 minute. | noshoot.png |
4 | richkid | Collect 10E10 coins in one session. | richkid.png |
4 | urdead | Die within 2 seconds. | urdead.png |
Omdat er geen enkele kolom voldoet aan de regels voor de primary key (uniciteit) zal de primary key bestaan uit de userId
en achievementType
.
Via de foreign key userId
kan je de tabel met gebruikers koppelen aan de tabel met scores:
---
title: 1e normaalvorm
---
erDiagram
USER ||--o{ SCORE : "userId"
2e normaalvorm¶
De regels voor de tweede normaalvorm zijn:
- voldoen aan de 1e normaalvorm
- geen partiële afhankelijkheden: ieder attribuut is afhankelijk van de gehele primary key
De tweede normaalvorm is alleen toe te passen met tabellen waarbij de primary key uit meerdere attributen bestaat (composite key) In de tabel met gebruikers is de primary key opgebouwd uit de userId
en de achievementType
. De achievementText
en achievementImage
zijn afhankelijk van de achievementType
. De tabel voldoet dus niet aan de tweede normaalvorm.
De tabel met gebruikers kan je normaliseren door een aparte tabel te maken met de achievements. De tabel met achievements ziet er dan als volgt uit:
achievementType | achievementText | achievementImage |
---|---|---|
noShoot | Don’t shoort enemies for 1 minute. | noshoot.png |
dodgy | Dodge 10 enemy bullets in one session. | dodgy.png |
richkid | Collect 10E10 coins in one session. | richkid.png |
urdead | Die within 2 seconds. | urdead.png |
De tabel voor de behaalde achievements ziet er dan als volgt uit:
userId | achievementType |
---|---|
1 | noShoot |
1 | dodgy |
1 | richkid |
2 | urdead |
2 | noShoot |
2 | dodgy |
3 | dodgy |
3 | richkid |
3 | urdead |
4 | noShoot |
4 | richkid |
4 | urdead |
De primary key van deze tabel bestaat uit de userId en de achievementType.
De tabellen zijn via hun foreign keys te koppelen:
---
title: 2e normaalvorm
---
erDiagram
ACHIEVEMENT }o--|| ACHIEVEMENTTYPE : "isOfType"
USER ||--o{ ACHIEVEMENT : "achieved"
USER ||--o{ SCORE : "scored"
3e normaalvorm¶
De regels voor de derde normaalvorm zijn:
- voldoen aan de 2e normaalvorm
- geen kolom mag afhankelijk zijn van een ander niet-key attribuut
In de achievement tabel is de achievementImage
afhankelijk van de achievementType
: de bestandsnaam van het plaatje komt direct overeen met de achievementType
. De tabel voldoet dus niet aan de derde normaalvorm. Door de kolom achievementImage
te verwijderen voldoet de tabel wel aan de derde normaalvorm. Bestandsnamen kunnen dan in code of met behulp van een SQL query worden gegenereerd.
De tabel met achievements ziet er dan als volgt uit:
achievementType | achievementText |
---|---|
noShoot | Don’t shoort enemies for 1 minute. |
dodgy | Dodge 10 enemy bullets in one session. |
richkid | Collect 10E10 coins in one session. |
urdead | Die within 2 seconds. |