Normalisering

Når vi designar ein relasjonsdatabase er det viktig å laga ein logisk og funksjonell struktur som medfører minst mulig redundans (dobbeltlagring) og reduserer sjansen for å introdusera inkonsistens i databasen gjennom såkalte anomaliar (meir om det nedanfor). For å oppnå dette vil ein normalt gjera ein normalisering av databasen. Det fins mange såkalte normalformer, og vi skal her sjå på dei tre første.

1NF: Første normalform

Normalformene er enklast å forstå ved å gi eksempel på brot på dei. Så her er ein tabell som ikkje er på første normalform:

Kunde ID Navn Telefon
123 Robert Ingram 555-861-2025
456 Jane Wright 555-403-1659, 555-776-4100
789 Maria Fernandez 555-808-9633

Navn er her ein ikkje-atomisk attributt, fordi den inneheld både for- og etternavn. Telefon er det som blir kalla for et repeterande attributt, fordi den inneheld fleire verdiar. Det første problemet er enkelt å løysa ved å dela det opp i to attributter. Det andre er litt verre, for ein slik tabell kan vi ikkje realisera i MySQL uten at Telefon blir eit tekstfelt, eller ved å gjera andre kunstgrep.

Vi kan tenkja oss følgande løysing:

Kunde ID Fornavn Etternavn Tlf1 Tlf2 Tlf3
123 Robert Ingram 555-861-2025

456 Jane Wright 555-403-1659 555-776-4100 555-403-1659
789 Maria Fernandez 555-808-9633

Her har vi innført to nye attributtar Tlf2, og Tlf3 for å ta vare på fleire telefonnr. Men vi står igjen med fleire problem- For det første: Det er mulig å skriva inn samme verdien inn i to av tlfnr. For det andre: Vi vil få problemer med å skriva eit query for å søkja etter eigaren til et telefonnr. For det tredje: enn om vi treng endå fleire tlnr? I såfall må vi endra på tabell-definisjonen, og det er upraktisk hvis vi alt har begynt å bruka den og leggja inn data. Måten å løysa disse problema er å splitta tabellen vår opp i to:

Kunde ID Fornavn Etternavn
123 Robert Ingram
456 Jane Wright
789 Maria Fernandez
 
Kunde ID Telefon
123 555-861-2025
456 555-403-1659
456 555-776-4100
789 555-808-9633

Her kan vi sørga for at kolonnen Telefon er unik. Då slepp vi å få inn like nr ved ein feil. Det er no enkelt å søkja på telefonnr., og dessuten kan vi skriva inn så mange tlfnr som vi vil på same kunde, for det blir bare nye rader i tabellen Kundetelefon, og vi har ikkje behov for å endra på tabellens utforming.

Første normalform betyr altså: Alle attributter er atomiske og ikkje-repeterande. Sjå eksempel

Determinering

For å forstå andre og tredje normalform, treng vi begrepet determinering: Hvis verdien av ein ein (eller eit sett av fleire) attributter A bestemmer verdien av ein attributt B, så seier vi at A determinerer B (A -> B) eller at B er funksjonelt avhengig av A. For eksempel determinerer postnr poststad. Når postnr (A) er gitt så er også poststad (B) bestemt. Poststad er altså determinert eller funksjonelt avhengig av postnr. Men det omvendte gjeld ikkje for det fins mange postnr med same poststad.

2NF: Andre normalform

For å vera på andre normalform, må tabellen vera på første normalform. Anta at vi har følgande tabell som beskriver ein ansatt sine ferdigheter. Den har ein samansett nøkkel beståande av Ansatt + Ferdighet:

Ansatt Ferdighet Arbeidsstad
Jones Typing 114 Main Street
Jones Shorthand 114 Main Street
Jones Whittling 114 Main Street
Bravo Light Cleaning 73 Industrial Way
Ellis Alchemy 73 Industrial Way
Ellis Flying 73 Industrial Way
Harrison Light Cleaning 73 Industrial Way

Vi ser at adressene gjentar seg fordi det er mange rader for same ansatte. 114 Main Street er det for eksempel tre versjonar av, og det betyr at hvis mr. Jones flyttar, så må vi gjera ein UPDATE på alle disse tre radene. Hvis vi ikkje gjer det får vi ein såkalt oppdateringsanomali som medfører at databasen vår blir inkonsistent: vi får fleire ulike adresser, og det vil vi helst unngå. Eit anna, men relatert problem, er hvis vi for eksempel vil leggja inn ein ny ansatt, men ikkje kjenner hans ferdigheter. Då kan vi ikkje utføra ein INSERT, fordi ferdighet er ein del av nøkkelen. Dette kallast ein insert-anomali. Vi har også eit tredje variant av same problem: hvis vi vil sletta ferdigheten "Light Cleaning" til den ansatte "Bravo", så klarer vi ikkje det uten samtidig å sletta den ansatte. Dette kallast ein delete-anomali. Til slutt må vi nevna at databasen også bruker vi meir plass enn nødvendig, men det er ofte ikkje så stort problem. Løysinga på problema våre er å laga to tabellar:

Navn Arbeidsstad
Jones 114 Main Street
Bravo 73 Industrial Way
Ellis 73 Industrial Way
Harrison 73 Industrial Way
 
Ansatt Ferdighet
Jones Typing
Jones Shorthand
Jones Whittling
Bravo Light Cleaning
Ellis Alchemy
Ellis Flying
Harrison Light Cleaning

Her er arbeidsstaden til Jones (og alle andre ansatte) skrive inn bare ein gong, slik at vi unngår inkonsistens i data. Men merk at vi fremdeles vil ha mange ansatte på samme arbeidsstad, så her kunne vi gått lenger ved å laga ein tabell som heiter arbeidsstad.

Når vi ser nærmare på dette eksempelet, legg vi merke til at i den første tabellen (Ansattes ferdigheter) har vi determinering frå Ansatt til Arbeidsstad, altså frå bare ein del av primærnøkkelen (som er samansett av Ansatt + Ferdighet), og det er dette som skaper problemet. Tabellen beskriv både ansatt og ferdighetane, og sidan det kan vera mange ferdigheter, får vi dobbeltlagring av Arbeidsstad.

Andre normalform betyr altså: 1NF er oppfylt + det fins ingen determineringer frå del av nøkkelfelt til andre felt som ikkje er nøkkel. Sjå eksempel

3NF: Tredje normalform

Her er et eksempel på ein tabell som oppfyller 2NF, men som ikkje oppfyller 3NF:

Turnering År Vinnar Vinnarens førdselsdato
Indiana Invitational 1998 Al Fredrickson 21 Juli 1975
Cleveland Open 1999 Bob Albertson 28 September 1968
Des Moines Masters 1999 Al Fredrickson 21 Juli 1975
Indiana Invitational 1999 Chip Masterson 14 Mars 1977

Problemet her er at vi fremdeles kan ha redundans, fordi vi kan ha samme vinnar over fleire år. Vi ser at Al Fredrickson vant både i 1998 og i 1991. Vi kan ikkje unngå å ha navnet hans to stader (med mindre vi innfører ein ID som kobling). men i tillegg til navnet, så er også Vinnarens fødelsdato dobbeltlagra i hans tilfelle. Her det altså igjen mulig å få inkonsistens, for det er ingenting som hindrar oss å skriva feil på (minst) ein av stadene. Men dette kan vi unngå, og igjen er løysinga å splitta tabellen i to:

Turnering År Vinnar
Indiana Invitational 1998 Al Fredrickson
Cleveland Open 1999 Bob Albertson
Des Moines Masters 1999 Al Fredrickson
Indiana Invitational 1999 Chip Masterson
 
Spiller Fødselsdato
Chip Masterson 14 Mars 1977
Al Fredrickson 21 Juli 1975
Bob Albertson 28 September 1968

Det er selvfølgelig fremdeles mulig å skriva inn feil fødselsdato, for det er ingenting som kan garantera det. Men det vi unngår er å få to eller fleire fødselsdatoer for same person i databasen vår. I dette eksempelet hadde vi determinering frå Vinnar til Vinnarens fødselsdato. Og dette skapte dobbeltlagring av Vinnarens fødselsdato.

Tredje normalform betyr altså: 1NF og 2NF må vera oppfylt + det skal ikkje vera determineringar frå et ikkje-nøkkelfelt til andre ikkje-nøkkelfelt. Sjå eksempel

Oppsummering

Når vi no oppsummerer dei tre normalformene, skal vi samtidig gjera ein liten presisering: Hvis vi i eksempel 2NF hadde introdusert ein ny primærnøkkel vha. eit ID, så ville vi likevel ha same problem med den førte versjonen av databasen. I dette tilfellet hadde Ansatt + Ferdighet blitt det som kallast en kandidatnøkkel dvs. felt som vi kunne ha valg å la vera primærnøkkel. Så når vi no set opp reglane, så erstattar vi "nøkkel" med "kandidatnøkkel":

1. normalform
Regel: Alle verdiar skal være atomære, og ingen felt får innehalda meir enn ein verdi.

2. normalform
Regel: Ingen ikke-nøkkelkolonner får være avhengig av delar av ein kandidatnøkkel.
Forutsetter: 1. normalform.

3. normalform
Regel: Ingen ikke-nøkkelkolonner får vera avhengig av noke anna enn kandidatnøklar.
Forutsetter: 2. normalform (og då også 1. normalform).

Når det gjeld determineringane under 2NF og 3NF kan du også bruka huskeregelen: "The key, the whole key, and nothing but the key" Og mange tilføyer då "so help me Codd". (Codd var pioneren innanfor relasjonsdatabaser :). Altså, alle determineringer skal gå frå heile nøkkelen og bare den.