YapfHoofdpagina | Info | Hulp | FAQ (veelgestelde vragen) | Speciale pagina's | Aanmelden


Printervriendelijke versie | Voorbehoud | Privacybeleid

Database ontwerp 101

Uit Yapf

Dit artikel legt grofweg uit hoe je een goed database ontwerp kunt maken. Er zitten een aantal onvolkomenheden in (benamingen, methodes) maar daar heb je bij de gemiddelde database verder geen last van.

Inhoud

Database ontwerp 101.

Dit artikel legt de basis uit van hoe je een goed database ontwerp kunt maken. Er zitten een aantal onvolkomenheden in (benamingen, methodes) maar daar heb je bij de gemiddelde database verder geen last van.

Dit is een hele lap tekst, maar lees het wel helemaal door. Het ontwerpen van een database is in principe vrij eenvoudig, maar je moet je aan een aantal regels houden. Het is belangrijk om te weten wat die regels zijn, maar vooral ook _waarom_ die regels er zijn, anders ga je absoluut de fout in en achteraf een normalisatiefout verbeteren kan een enorme klus worden omdat je dan meestal al een hele serie queries en scripts hebt die er gebruik van maken.


De database uit dit artikel is ontworpen met behulp dan Dezign for Databases, van het Nederlandse bedrijf Datanamic, welke met flinke korting te verkrijgen is voor leden van de PHPFreakz vereniging

Voorbereiding.

Een goed database ontwerp begint op papier, met een opsomming van de gegevens die je in de database wilt hebben en wat je later allemaal met de database wil gaan kunnen. Dit gaat allemaal in gewoon Nederlands, zonder een woord SQL. In dit stadium moet je proberen om niet in tabellen en kolommen te denken maar alleen in gewoon nuchter "wat moet ik allemaal weten?" Denk hier niet te licht over, want als je er later achter komt dan je iets bent vergeten dan kun je meestal overnieuw beginnen. Dingen toevoegen aan een database is meestal een berg werk.

Als je voor een klant werkt, ga dan met die klant praten en probeer er achter te komen welke gegevens hij gebruikt en hoe die gegevens met elkaar te maken hebben. Vraag hem om je uit te leggen hoe hij zijn werk doet. Maak niet de klassieke fout om de klant te vragen welke gegevens hij wil opslaan want dat weet hij niet. Ga gewoon naast hem zitten en kijk wat zijn werkzaamheden precies zijn. Elke keer dat hij een formuliertje pakt maak jij een kopie en schrijf je op wat hij ermee doet. Breng zijn werk in kaart, alleen daarmee kun je bepalen wat de database moet gaan opslaan.

Blijf ook niet bij de baas hangen, praat met iedereen die met de database te maken krijgt. De baas is meestal degene die het minst weet van wat zijn werknemers doen.

Stap 1. Bepaal de entiteiten

De soorten informatie die in de database wordt opgeslagen heten 'entiteiten'. Deze zijn er in vier soorten: mensen, gebeurtenissen, lokaties en voorwerpen. Alles wat je in een database zou kunnen willen zetten past in deze categorieen. Pas het er niet in, dan is het geen entiteit maar een eigenschap van een entiteit.

Voorbeeld

Stel je maakt een website voor een winkel (niet perse een webwinkel), waar heb je dan allemaal mee te maken?

Wat er in een winkel gebeurd is redelijk simpel: "Een klant komt binnen in het filiaal en koopt één of meerdere producten."

Welke entiteiten zitten er in dit ene zinnetje: De winkel is een lokatie verkoop is een gebeurtenis, producten is een voorwerp en klanten zijn mensen.

Maar is dit alles? Vraag altijd om details! Kan de klant zelf een koop tot stand brengen? Nope, daar is de verkoper voor. Verkopers zijn mensen, dus verkopers wordt ook een entiteit.


Image:database_tut_entities.gif

Stap 2. Relaties en hun cardinaliteit

De volgende stap is bepalen wat de relaties tussen de verschillende entiteiten zijn en wat de cardinaliteit van elke relatie is.

Relaties

De relatie is de verhouding tussen de entiteiten, gewoon zoals in de echte wereld; wat doet de ene entiteit met de ander, hoe hebben ze met elkaar te maken?

Let op': Een relatie gaat altijd twee kanten op; als A met B te maken heeft dan heeft B per definitie ook met A te maken. Beschrijf de relatie altijd voor beide richtingen!

In het winkelvoorbeeld geldt dat:


Cardinaliteit

De cardinaliteit geeft aan hoeveel van de ene kant van de relatie horen bij hoeveel van de andere kant. In eerste instantie geef je voor elke relatie aan hoeveel van de ene kant er horen bij precies 1 van de andere kant; Hoeveel klanten horen bij 1 verkoop? Hoeveel verkopen horen er bij 1 klant? Hoeveel verkopen vinden er plaats in 1 winkel?

In het webwinkel voorbeeld is het relatiediagram alsvolgt: (onthoud dat product hier staat voor een soort product, niet voor losse stuks!)

Nu gaan we deze gegevens samenvoegen om de cardinaliteit van de hele relatie te vinden. Hiervoor gaan we de cardinaliteiten optellen per relatie. Om dit makkelijk te doen veranderen we de notatie een beetje door de 'terug' relatie andersom te noteren:

Die laatste draaien we om zodat hij dezelfde entiteitvolgorde heeft als de eerst. Let op de pijl die nu andersom staat!

Cardinaliteit is er in vier soorten: een-op-een, een-op-meer, meer-op-een, meer-op-meer. In database ontwerp wordt dit aangeven met respectievelijk: 1:1, 1:N, M:N en M:1. Om de juiste term te vinden laat je alle '1' gewoon staan. Als er aan de linker kant 'meer' staat wordt dat 'm' en als er aan de rechter kant 'meer' staat wordt dat 'm'.

De echte cardinaliteit kun je nu berekenen door domweg de grootste waarde voor links en rechts te nemen, waarbij 'n' of 'm' dus groter zijn dan '1'. In dit geval staat er links in beide gevallen een '1', dus dat blijft '1'. Aan de rechter kant staat een 'N' en een '1', de 'N' is de grootste dus dat wordt 'N'. De totale cardinaliteit is dus '1:N'. Een klant heeft meerdere 'kopen', maar elke 'koop' heeft maar één klant.

Als je dit voor de andere relaties ook doet krijg je:

We hebben dus drie keer een 1-op-meer relatie, en drie keer een meer-op-meer relatie.

Let op: meer wordt ook wel veel genoemd. Een meer-op-meer relatie is hetzelfde als een veel-op-veel relatie en dat is weer hetzelfde als een M:N relatie.

Stap 3. Bepaal Attributen

De gegevens die je over elke entiteit wilt kunnen opslaan heten "attributen".

Van de producten die je verkoopt weet je bv wat de bestelcode is, wat de prijs is, wat de naam van de fabrikant is en wat het typenummer is.

Van de klanten weet je het klantnummer, de naam, adres, telefoonnummer.

Van de winkels weet je de locatiecode, de naam, adres.

Van de verkopen weet je wanneer ze gebeurd zijn, in welke winkel, welke producten er zijn verkocht, aan welke klant er is verkocht en voor hoeveel geld er is verkocht.

Van de verkoper weet je het werknemernummer, naam, adres.

Wat er precies in al die attributen staat is nu nog niet interessant. Het gaat alleen om wat je wilt opslaan.

image:erd_met_attributen_fout.gif

De oplettende lezer ziet nu in de verkopentabel iets vreemds gebeuren; aparte attrbuten voor elk verkocht product. Dit wordt later opgelost.

De Primary Key

De primary key (ook wel PK genoemd) is een attribuut of verzameling attributen die het mogelijk maken om een record uniek aan te kunnen wijzen. Als een attribuut als PK wordt gedefinieerd dan moeten alle attributen die in die PK zitten altijd in elk record een waarde hebben (je mag het dus niet leeg laten) en de combinatie van waardes binnen de attributen moet samen uniek zijn in de tabel. Elke entiteit in een database model heeft precies één primary key, maar die primary key kan wel bestaan uit meerdere attributen.

In het voorbeeld bestaan een aantal duidelijke kandidaten voor de PK. Klanten hebben elk een unieke klantcode, producten hebben een unieke productcode en de verkopen hebben een verkoopnummer. Elk van deze gegevens is uniek en elk record zal zo'n waarde bevatten, dus deze attributen kunnen PK worden. Voor de primary-key wordt vaak een numerieke kolom genomen zodat je een record eenvoudig kunt opzoeken aan de hand van een getal. Getallen nemen in de database ook weinig ruimte in, met 4 bytes per getal kun je al tot meer dan 4 miljard tellen, maar je mag in principe elke soort waarde als PK gebruiken.

Koppeleintiteiten

In koppel-entiteiten (zoals die die meer-op-meer relaties oplossen) wordt meestal verwezen naar de PK attributen van de entiteiten die ze koppelen. De PK van een koppel-entiteit is meestal weer een verzameling van deze verwijzende attributen zodat b.v. een klant niet twee keer aan dezelfde verkoop gekoppeld kan worden. In dit geval worden de velde ook wel PF's genoemd, Primary-Foreign-keys, omdat ze Primary-key zijn voor de entiteit waar ze in staan, maar Foereign-key zijn omdat ze verwijzen naar een waarde die uit de Primary-key van een andere entiteit komt.

In het ERD worden de PK attrubuten aangegeven door de tekst 'PK' voor of achter de naam van het attribuut te zetten. In het voorbeeld heeft eigenlijk alleen de 'winkel' entiteit niet echt een duidelijke kandidaat voor de PK, dus daar verzinnen we een nieuw attribuut voor; winkelnummer.

De Foreign key

De Foreign Key in een entiteit is de verwijzing naar de primary key van een andere entiteit In het ERD krijgt dat attribuut 'FK' achter de naam. De foreign-key van een entiteit kan zelf ook weer deel uitmaken van de primary key van die entiteit en in dat geval staat er 'PF' achter het attribuut. Dit is meestal het geval bij de koppel-entiteiten, omdat je twee instanties van de twee entiteiten meestal maar een keer aan elkaar koppelt. (bij 1 verkoop wordt 1 procucttype maar 1x verkocht. Er kunnen wel meerdere stuks van worden verkocht, maar het type wordt maar 1x verkocht)


Derived data

Derived data betekent 'afgeleide data'. Gegevens die voortkomen uit andere gegevens die je al hebt opgeslagen. In dit geval is de 'totaalprijs' in 'verkopen' een klassiek geval van derived data. Je weet precies wat er is verkocht en wat elk product kost, dus kun je altijd uitrekenen hoeveel er in totaal is betaald. In principe is het dus niet nodig om het totaal op te slaan.

Waarom doe ik het hier dan wel? Omdat het hier gaat om een verkoop en de prijs van het product kan varieren met de tijd. Een product kan vandaag 10 euro kosten en volgende maand 8 euro, maar voor de administratie moet je weten wat het kostte op het moment van de verkoop en de eenvoudigste manier om dat te doen is door het hier op te slaan. Er zijn veel elegantere manieren, maar dat gaat te ver voor dit artikel.

Voorbeeld

Met keys ziet het model er nu zo uit:

image:erd_met_attributen_fout_metkeys.gif

Stap Bepaal de soort relaties

Onderlinge afhankelijkheid / verplichte relaties

Onderlinge afhankelijkheid bestaat als het een niet kan bestaan zonder het ander. In dit geval kan er bijvoorbeeld geen verkoop plaatsvinden als er geen klant is om de verkoop te doen en er kan geen verkoop plaatsvinden als er geen producten zijn om te verkopen.

De relaties verkoop -> klanten en verkoop -> producten zijn dus verplicht, maar omgekeerd is dat niet het geval: een klant kan prima bestaan zonder ooit iets te kopen en een product hoeft niet perse verkocht te zijn om te kunnen bestaan.

Recursive relationships

Recursive relaties. Soms verwijst een entiteit terug naar zichzelf. Denk bijvoorbeeld aan een hierarchie van werknemers. Een vakkenvuller heeft een meerdere en dat is de de chef, maar die chef is zelf ook een werknemer en heeft zelf ook een meerdere, namelijk de directeur. Het attribuut meerdere van de werknemersentiteit verwijst dus teerug naar de werknemersentiteit.


Identifying & non-identifying relationships

Een Identifying relationship is een relatie waarin de PK van de ene tabel onderdeel wordt van de PK van de gerelateerde tabel. Dit is bijvoorbeeld vaak het geval bij koppelentiteiten.

Verplichte / Mandatory relationships

Een verplichte relatie is een relatie waarin de FK altijd ingevuld moet zijn. Dit is bijvoorbeeld het geval bij het klantnummer van een verkoop; een verkoop mag nooit worden gemaakt zonder een klantnummer erin.

Bij een niet-verplichte relatie hoeft de FK niet ingevuld te zijn. Let op dat niet elke database dit aan kan.


Stap Teken het Entity Relation Diagram (ERD)

Notatie

Het ERD geeft een grafisch overzicht van de database. Zoals overal zijn er ook voor het ERD meerdere stijlen en soorten dus vooraf even een overzicht van de stijl die in dit artikel wordt gebruikt.

Deze notatie heet de kraaienpoot notatie, hierbij worden entiteiten als rechthoeken weergegeven en de relaties tussen de entiteiten worden weergegeven als lijnen van entiteit naar entiteit. De soort relatie wordt aangegeven door aan de uiteinden tekens te plaatsen. De kant waar er verplicht record(s) aanwezig moeten zijn krijgt een streep haaks op de lijn. Niet-verplicht wordt aangegeven met een cirkel ipv een streep. Meer wordt aangegeven door een kraaienpoot; de relatie-lijn splitst zich in meerdere (meestal drie) lijnen. Een 1:1 relatie wordt dus:

Image:database_tut_11.gif

Een 1:N relatie wordt:

Image:database_tut_1n.gif

Een M:N relatie wordt:

Image:database_tut_mn.gif

Een recursieve relatie is een mooie lus die bij de entiteit zelf terugkomt:

Image:recursive_example.gif


Het model van het voorbeeld komt er dan zo uit te zien:

Image:erd_alleenrelaties.gif

Stap Relaties opschonen

Er zijn een aantal relatiesoorten die je niet in een datamodel wilt hebben en die moet je dus oplossen. Dit zijn standaardproblemen met standaardoplossingen dus het stelt niets voor.

Redundant Relationships.

Soms krijg je in je model zogenaamde redundant relationships. Redundant betekent overbodig, dubbelop, niet noodzakelijk. Een redudant relationship is een relatie die op meerdere manieren tegelijk gemaakt wordt in het ERD.

In het geval van dit voorbeeld is er een directe relatie tussen klanten en producten, maar er zijn ook relaties van klanten naar verkopen en van verkopen naar producten, dus indirect is er via verkoop ook een relatie tussen klant en product. De relatie klant<->product wordt dus twee keer gemaakt en een daarvan is dus 'redundant'. In het geval van de winkel worden producten altijd via een verkoop aan de klant verkocht dus dus de relatie klant<->product mag weg.

Het oplossen van meer-op-meer relaties.

Meer-op-meer relaties (M:N) zijn niet rechtstreeks mogelijk in een database. Wat een M:N relatie zegt is dat een aantal records uit de ene tabel hoort bij een aantal records uit de andere tabel. Ergens moet je dus gaan opslaan om welke records het precies gaat en de oplossing is dan ook om de relatie op te splitsen in twee één-op-meer relaties.

Dit gebeurt door een nieuwe entiteit aan te maken die tussen de twee gerelateerde entiteiten in zit. In dit geval is er een meer-op-meer relatie tussen verkopen en producten. En dat wordt opgelost door een nieuwe entiteit te maken; verkoop-detail. Die entiteit krijgt een meer-op-één relatie met verkopen, en een één-op-meer relatie met producten. (samen dus weer meer-op-meer)

Voorbeeld

In het voorbeeld zit een meer-op-meer relatie die moeten worden opgelost: producten<->winkels. Deze relatie geeft aan welke producten er in welke winkels liggen en dat is dus gewoon de voorraad van de winkel.

En met alle attributen en keys erin wordt het:

image:erd_met_attributen_keys_notypes_denorm.gif

Normalisatie

Normalisatie maakt je model flexibel en betrouwbaar. Het genereert wel wat overhead omdat je meestal meer tabellen krijgt, maar daar staat tegenover dat je met je datamodel alle kanten op kunt zonder dat je het hoeft aan te passen om uitbreidingen en veranderingen te kunnen verwerken.

Normalisatie, de eerste vorm

De eerste vorm van normalisatie zegt dat er in een tabel geen herhalende kolommen/attributen mogen zijn. In de entiteit verkopen zitten vier productenattributen, dat is zo'n herhalende kolom

Wat hier fout aan is is dat er nu maar vier producten kunnen worden verkocht. Wil je er vier verkopen, dan moet je ofwel een tweede verkoop starten, of je datamodel aanpassen door 'product5' toe te voegen. Dit is natuurlijk allebei volkomen ongewenst.

Wat je hier hebt is gewoon een M:N relatie met de beperking dat N niet hoger kan zijn dan 4. De oplossing is dan ook dezelfde als voor de M:N relatie: maak een koppelentiteit. In dit geval 'verkoop_detail

image:erd_met_attributen_keys_notypes_norm.gif

Normalisatie, de tweede vorm

De tweede vorm zegt dat alle attributen van een entiteit functioneel afhankelijk moeten zijn van de hele primary-key. Dat betekent dat je elk attribuut van een entiteit alleen kunt identificeren middels de gehele primary-key.

Normalisatie, de derde vorm

De derde vorm zegt dat alle attributen rechtstreeks afhankelijk moeten zijn van de primary-key, en niet van andere attributen.Dit lijkt op wat de 2e normaalvorm zegt, maar in de 2e normaalvorm wordt eigenlijk het omgekeerde gezegd. In de 2e normaalvorm wijs je attributen aan via de PK, in de 3e normaalvorm moet elk attribuut alleen afhankelijk zijn van de PK, en niets anders.

Normalisatie, nog meer vormen

Normalisatie kent nog veel meer vormen, maar die zijn voor de gewone gebruiker niet echt interessant. Deze vormen zijn zeer gespecialiseerd voor bepaalde toepassingen. Als je je houdt aan de ontwerp regels en de normalisatie, dan kom je op een ontwerp dat voor de meeste toepassingen prima voldoet.


Stap Bepaal de datatypes van de attributen

Nu is het tijd om uit te zoeken welke datatypes er gebruikt moeten worden voor de verschillende attributen. Database sterven van de datatypes. Een aantal is gestandaardiseerd maar veel databases hebben hun eigen datatypes met elk hun eigen voordelen. De betere databases zoals PostgreSQL maken het zelfs mogelijk om je eigen datatypes te definieeren voor het geval de standaard types niet kunnen wat je nodig hebt.

De standaard datatypes die elke database kent zijn: CHAR, VARCHAR, TEXT, FLOAT, DOUBLE en INT. Dit zijn ook de meest-gebruikte.

Voor meer details kun je beter kijken in de handleiding van je database, zoals PostgreSQL of MySQL

Tekst

VARCHAR(lengte) - hetzelfde als CHAR, maar bij VARCHAR wordt er alleen zoveel ruimte gebrukt als noodzakelijk. TEXT - kan zeer grote lappen tekst bevatten. Afhankelijk van het merk database kan dit oplopen tot gigabytes.

Getallen

Andere types

Voorbeeld

Voor het voorbeeldmodel worden de datatypes alsvolgt:

Afbeelding:Erd met atributen met keys goed denorm.gif

Het uitgewerkte model

Als je de normalisatieregels toepast kom je eigenlijk alleen tegen dat de fabrikant in de producten tabel ook een aparte tabel hoort te zijn:

Naslag

Woordenlijst

Normalisatie in een notendop

Kort samengevat zijn dit de regels waar je je bij een ontwerp aan moet houden:

Teruggeplaatst van "http://www.yapf.net/index.php/Database_ontwerp_101"

Deze pagina is 6.598 maal bekeken. Deze pagina is het laatst bewerkt op 13 mrt 2010 om 16:36.


Zoeken

Bladeren
Hoofdpagina
Gebruikersportaal
In het nieuws
Recente wijzigingen
Willekeurige pagina
Hulp
Bewerken
Brontekst bekijken
Hulp bij bewerken
Paginaopties
Overlegpagina
Opmerking toevoegen
Printervriendelijke versie
Pagina-informatie
Paginageschiedenis
Verwijzingen naar deze pagina
Verwante wijzigingen
Mijn pagina's
Aanmelden / registreren
Speciale pagina’s
Nieuwe pagina's
Bestandslijst
Statistieken
Foutrapporten
Meer …