Det här är ett avsnitt i en webbkurs om databaser som finns fritt tillgänglig på adressen http://www.databasteknik.se/webbkursen/. Senaste ändring: 18 juli 2005.

Av Thomas Padron-McCarthy. Copyright, alla rättigheter reserverade, osv. Skicka gärna kommentarer till webbkursen@databasteknik.se.

Normalformer och normalisering

Normalformer och normalisering är en teori för relationsdatabaser, som man kan använda för att undvika att få en dum design på sin databas. Med "dum design" menar vi att man skapar tabeller så att vissa data kommer att dubbellagras, medan andra data kanske inte alls går att lagra.

Det kan vara bra att läsa kursavsnittet om relationsmodellen först.

Varför ska jag lära mig det här?

Om man börjar med att rita ett ER-diagram (eller motsvarande) som man sen översätter till tabeller, så blir det för det mesta rätt. Problem med normalisering brukar man få om man i stället försöker pussla ihop tabeller redan från början. Därför slipper man många normaliseringsproblem om man börjar med att rita ett ER-diagram. De problem som ändå uppstår kan man ofta hantera om man följer den enkla grundregeln om en typ av sak per tabell, och en sån sak per rad.

Men teorin om normalisering behövs ändå:

Ett förklarande exempel

Vi antar att vi har ett företag som köper varor från olika leverantörer, och att vi vill hålla reda på våra data med hjälp av en databas.

Vi vill hålla reda på vilka varor (till exempel bilar) vi köper, och från vilka leverantörer (till exempel Volvo) vi köper dem. Vi kan köpa varje vara från olika leverantörer.

Dessutom vill vi veta varornas pris. Priset för en vara är förstås olika beroende på från vilken leverantör vi köper den.

Vi vill också lagra leverantörernas adresser i databasen, dvs i vilken stad varje leverantör ligger. Vi antar nu att varje leverantör bara finns i en enda stad.

Kanske behöver vi plötsligt beställa väldigt många bilar från Volvo. Då kan det vara bra att veta hur många människor som bor i staden där Volvo ligger, så att vi vet om Volvo snabbt kan nyanställa folk för att tillverka bilarna. Därför lagrar vi folkmängden för varje stad.

Första försöket att göra en databas

Vi provar med en tabell som vi kallar Inköp (tabell 1):

Vara Leverantör Pris Stad Folkmängd
Bilar Volvo 100000 Torslanda 80000
Bilar Saab 150000 Södertälje 50000
Lastbilar Saab 400000 Södertälje 50000
Magnecyl Astra 10 Södertälje 50000

Vara och Leverantör bildar tillsammans primärnyckel.

Det här är inte någon bra lösning.

Lösningen är förstås att dela upp tabellen i flera. Men hur?

Enkel regel för hur tabeller ska se ut

Grundregeln är att varje tabell ska beskriva en typ av sak, varje rad i tabellen ska innehålla data om en enda sådan sak, och de data vi lagrat för varje sak ska finnas på en enda rad. Exempelvis kan vi ha en tabell som beskriver leverantörer, där varje rad innehåller data om en leverantör. Alltså: en typ av sak per tabell, en sak per rad, och en rad per sak.

Exempel: Om vi ska lagra information om anställda, så skapar vi en tabell som heter Anställd, och där varje rad handlar om en anställd. Vi kan till exempel ha de här kolumnerna:

Vi ska inte ha de här kolumnerna:

Ofta räcker det med det den här enkla regeln. Om man bara följer regeln om en typ av sak per tabell, en sak per rad, och en rad per sak, så kommer ens databaser att få en bra design, och man undviker problemen med redundans, saker som inte går att lagra, och tabeller som är svåra att förstå.

Men ibland är det svårt att riktigt veta vad det är för "saker" man vill lagra, och vilka data som egentligen hör ihop med dem. Då har vi nytta av teorin om normalisering. Den hjälper oss att se exakt hur de olika kolumnerna i tabellen hör ihop, och visar oss hur vi ska dela upp tabellen för att slippa problemen. Därför börjar vi nu titta på de olika normalformer som teorin om normalisering beskriver. Normalformer är villkor som en tabell kan uppfylla. Det enklaste villkoret är första normalformen, och genom att lägga på fler villkor kan man definiera andra normalformen, tredje normalformen, och så vidare.

Första normalformen, "1NF"

Första normalformen säger bara att tabellen ska innehålla atomära värden, dvs högst ett värde per ruta. Exempelvis kan vi i tabellen ovan inte peta in både Volvo och Saab i samma ruta, även om vi köper bilar från båda leverantörerna. Vi måste använda två olika rader för att lagra detta. I de flesta relationsdatabashanterare går det helt enkelt inte att stoppa in mer än ett värde i varje ruta, så alla tabeller är i första normalformen.

Eller nästan i alla fall. Det går förstås att göra ett textfält och sen stoppa in flera namn, med till exempel komma mellan dem: Saab,Volvo,Renault. Ett problem med det är att det blir ganska svårt att göra sökningar i databasen, eftersom SQL-frågorna blir krångliga. Det är inte meningen att man ska göra så, och databashanterarna är inte byggda för att klara det på ett enkelt sätt.

Funktionellt beroende, "fb"

Om vi tittar på vår exempeltabell, tabell 1, så inser vi att på varje rad där det står Södertälje i kolumnen Stad, så kommer det att stå 50000 i kolumnen Folkmängd. Det kallas att kolumnen Folkmängd är funktionellt beroende av Stad.

Mer formellt kan vi säga att om värdet på ett (eller flera) attribut A entydigt bestämmer värdet på ett annat attribut B, så är B funktionellt beroende av A. "Entydigt bestämmer" betyder att om värdena på A på två rader i tabellen är lika, så måste värdena på B också vara lika. Det kan skrivas med en pil: A -> B. Vi kallar A för en determinant, eftersom den bestämmer ("determinerar") B.

I exempeltabellen finns dessa funktionella beroenden:

Vi ritar upp attributen, med de funktionella beroendena som pilar:

Funktionella beroenden i den ursprungliga tabellen

I fortsättningen förkortar vi ibland "funktionellt beroende" till "fb".

Fullständigt funktionellt beroende, "ffb"

Men vänta nu! Det finns ju fler funktionella beroenden i den tabellen!

Vi har sett att på varje rad där det står Södertälje i kolumnen Stad, så kommer det att stå 50000 i kolumnen Folkmängd. Detta betydde, sa vi, att kolumnen Folkmängd är fb av Stad.

Men då kan vi också säga så här:

Vi har sett att på varje rad där det står Södertälje i kolumnen Stad, och där det står Bil i kolumnen Vara, så kommer det att stå 50000 i kolumnen Folkmängd. Alltså är kolumnen Folkmängd fb av kombinationen Stad och Vara!

Det är ju lite fånigt, så därför definierar vi något som kallas fullständigt funktionellt beroende, som är ett funktionellt beroende där man inte kan ta bort några attribut ur determinanten om det fortfarande ska vara ett funktionellt beroende. Man kan också säga att determinanten är minimal.

I fortsättningen talar vi hela tiden om fullständiga funktionella beroenden, och när vi talar om en determinant menar vi en (eller flera) kolumner som en annan kolumn är ffb av.

I fortsättningen förkortar vi ibland "fullständigt funktionellt beroende" till "ffb".

Hur vet man vilka funktionella beroenden som finns?

Vilka fullständiga funktionella beroenden finns i den här tabellen (tabell 2)?

A B C D
1 4 10 100
2 5 20 50
3 6 20 200
1 4 10 200
2 6 20 0
3 6 20 300
1 4 10 null
2 6 20 50
3 6 20 50

Svaret är att det vet vi inte! Vilka beroenden som finns beror inte på vilka data som för tillfället råkar finnas i tabellen, utan det beror på logiken bakom tabellen.

Däremot kan man se vilka ffb som kan finnas, genom att de inte motsägs av de data som finns i tabellen.

Ffb som kan finnas

Alltså A -> C och B -> C.

Exempelvis kan det inte finnas ett ffb A -> B, eftersom det för samma värde på A (nämligen 2) förekommer olika värden på B (5 och 6).

Tänk på att determinanter kan vara sammansatta av flera attribut. I exemplet ovan finns det dock inga sådana ffb. Exempelvis kan det inte finnas ett ffb (A, B) -> D, eftersom det för samma värde på (A, B) förekommer olika värden på D. Det kan inte heller finnas ett ffb (B, D) -> C, men det beror på att det finns ett ffb B -> C, och alltså är (B, D) -> C visserligen ett funktionellt beroende, men inte ett fullständigt sådant.

Andra normalformen, "2NF" (ett första, dåligt, försök)

Titta på tabellen Inköp (tabell 1) igen. För varje vara som vi köper från Saab, måste vi upprepa informationen att Saab ligger i Södertälje. Det beror på att informationen om Saab "hänger ihop" med själva namnet Saab, och all den Saab-informationen följer med varje gång vi har med Saab i tabellen. Om tabellen hade handlat om leverantörer, med en leverantör på varje rad, så hade Saab bara varit med en gång, och då hade Saab-informationen bara stått på ett ställe. Men nu handlar tabellen om inköp, och vi köper flera varor från Saab, så därför kommer Saab-informationen med flera gånger.

Detta är uppenbarligen en dum design, och mot just den här sortens dumma design hjälper andra normalformen.

Andra normalformen säger att en tabell, förutom att vara i första normalformen, inte får innehålla några fullständiga funktionella beroenden på delar av primärnyckeln. Om man ritar upp de fullständiga funktionella beroendena mellan attributen, får det alltså inte finnas några pilar från delar av primärnyckeln, bara från hela primärnyckeln.

I vår exempeltabell bestod ju primärnyckeln av de två attributen Vara och Leverantör, medan det fanns två fullständiga funktionella beroenden från attributet Leverantör: Dessa strider mot 2NF, så vi delar upp tabell 1 och skapar två nya tabeller.

En ny tabell Inköp (tabell 3), där Vara och Leverantör fortfarande är primärnyckel:

Vara Leverantör Pris
Bilar Volvo 100000
Bilar Saab 150000
Lastbilar Saab 400000
Magnecyl Astra 10

Och en tabell Leverantör (tabell 4), med Leverantör som primärnyckel. (Kanske vill man byta namn på kolumnen Leverantör till Namn, för det är ju namnet på leverantören, inte leverantörens leverantör.)

Leverantör Stad Folkmängd
Volvo Torslanda 80000
Saab Södertälje 50000
Astra Södertälje 50000

Dessa tabeller uppfyller 2NF. Nu står det bara på ett ställe att Saab ligger i Södertälje. Vi kan lägga in Gnesta-Kurres korvkiosk som leverantör, trots att vi ännu inte köper några varor därifrån.

Redundans i alla fall?

Någon kanske tycker att vi fortfarande har redundans, eftersom namnet Saab står med två gånger i den nya tabellen Inköp. Informationsmässigt är det egentligen ingen redundans, eftersom varje förekomst av namnet Saab ger den nya informationen att en viss vara levereras av Saab. Däremot är det förstås så att långa namn (till exempel Svenska Aeroplanaktiebolaget) tar upp plats, särskilt om man tagit till lite extra på textfältets storlek. Därför är det vanligt att man hittar på ett särskilt nummer, som man kan använda som nyckel i en tabell. I det här exemplet kan man ge varje leverantör ett nummer, och sen använder man det numret för att referera till leverantören. Så här:

Inköp (tabell 5)

Vara Leverantör Pris
Bilar 1 100000
Bilar 2 150000
Lastbilar 2 400000
Magnecyl 3 10

Leverantör (tabell 6)

Nummer Namn Stad Folkmängd
1 Volvo Torslanda 80000
2 Saab Södertälje 50000
3 Astra Södertälje 50000

En bättre definition av 2NF

Definitionen av 2NF här ovanför gäller i en tabell med en enda kandidatnyckel, som då förstås också är primärnyckel. Denna primärnyckel kan förstås vara sammansatt av flera attribut, men det finns ingen annan (minimal) kolumnkombination som garanterat är unik för varje rad.

Men det kan ju finnas flera kandidatnycklar i en tabell. I så fall måste vi tänka på alla kandidatnycklarna och inte bara primärnyckeln, om vi verkligen ska få bort de problem som 2NF ska lösa.

Det kan vi se genom ett exempel. Antag att vi inför ett unikt nummer på varje inköpssamband, och lägger till det som en kolumn i den ursprungliga tabellen Inköp (tabell 1). Då blir det numret en kandidatnyckel, förutom kombinationen av Vara och Leverantör. Tabell 7:

Nummer Vara Leverantör Pris Stad Folkmängd
1 Bilar Volvo 100000 Torslanda 80000
2 Bilar Saab 150000 Södertälje 50000
3 Lastbilar Saab 400000 Södertälje 50000
4 Magnecyl Astra 10 Södertälje 50000

Om vi väljer kombinationen Vara och Leverantör som primärnyckel, precis som förut, så går det bra. Tabellen uppfyller inte 2NF, och måste delas upp. Men om vi väljer attributet Nummer som primärnyckel, så kommer tabellen att vara i 2NF redan från början! Alla icke-nyckelattributen är ju beroende av hela primärnyckeln. (Något annat vore konstigt, eftersom primärnyckeln inte är sammansatt!) Alla problemen, till exempel med redundans, kvarstår, trots att tabellen är i 2NF.

Därför vill vi ha en definition av 2NF som fungerar även med flera kandidatnycklar:

Tredje normalformen, "3NF"

När vi gjorde om den ursprungliga tabellen till 2NF försvann en del problem. Men vi är inte klara än. Det står fortfarande på flera ställen att det bor 50000 personer i Södertälje, och vi kan inte lägga in en stad där vi inte har några leverantörer. För att lösa dessa problem måste vi ta till tredje normalformen.

Tredje normalformen säger att en tabell, förutom att vara i andra normalformen, inte får innehålla några transitiva beroenden till icke-nyckelattribut. Det får alltså inte finnas några pilar som går mellan attribut utanför de olika kandidatnycklarna, bara (antingen) från kandidatnycklar till attributen utanför, eller från attributen utanför in i kandidatnycklarna. (Det betyder att om man har en sammansatt primärnyckel, så kan man ha pilar som pekar på ett av attributen i nyckeln.)

Vi delar upp tabellen Leverantör (tabell 4) och skapar två nya tabeller:

En ny tabell Leverantör (tabell 8), med attributet Leverantör som primärnyckel:

Leverantör Stad
Volvo Torslanda
Saab Södertälje
Astra Södertälje

En tabell Stad (tabell 9), med attributet Stad som primärnyckel:

Stad Folkmängd
Torslanda 80000
Södertälje 50000

Dessa tabeller uppfyller 3NF.

Hur vet man hur man ska göra uppdelningen?

Tänk om vi hade delat upp tabellen Leverantör (tabell 4) så här i stället:

En ny tabell Leverantör (tabell 10), med attributet Leverantör som primärnyckel:

Leverantör Stad
Volvo Torslanda
Saab Södertälje
Astra Södertälje

Och en tabell Stad (tabell 11), också med attributet Leverantör som primärnyckel! I den tabellen kan man läsa hur stor folkmängden är i den stad som en viss leverantör finns i:

Leverantör Folkmängd
Volvo 80000
Saab 50000
Astra 50000

Dessa tabeller uppfyller också 3NF! Det finns ju inga transitiva beroenden. Men uppenbarligen var det en ganska korkad uppdelning. Alla problemen som vi försökte lösa med hjälp av 3NF finns kvar.

Regel: Låt därför bli att göra korkade uppdelningar. Tänk på vad tabellerna betyder.

En ännu dummare uppdelning vore den här (tabell 12 och 13):

Leverantör Stad
Volvo Torslanda
Saab Södertälje
Astra Södertälje
        
Folkmängd
80000
50000

Tabellerna uppfyller 3NF, men med de här två tabellerna kan vi inte återskapa informationen i den ursprungliga leverantörstabellen. Vi vet inte vilken folkmängd som hör till vilken stad. Rader i tabellerna i en relationsdatabas har ju ingen bestämd ordning, så vi vet inte om 80000 hör ihop med Torslanda eller Södertälje.

Regel: Låt också bli att göra ännu dummare uppdelningar.

Faktaruta om "lossless join decomposition":

Den här sortens "ännu dummare uppdelning" beskrivs i en del databasböcker med något som kallas "lossless join decomposition", eller "uppdelning som ger en förlustfri join".

När man ska återskapa den ursprungliga informationen så använder man operationen join för att slå ihop de två nya tabellerna till en. Men vi har ju tappat bort en del information när vi gjorde uppdelningen, och det går inte att få fram den ursprungliga tabellen. Det går inte att få fram om det bor 50000 eller 80000 människor i Södertälje. Vi har alltså inte lyckats göra en "lossless join decomposition".

Boyce-Codds normalform, "BCNF"

3NF tillät ju fullständiga funktionella beroenden in i primärnyckeln, dvs det var tillåtet med pilar från icke-nyckelattribut till attribut i nyckeln. Boyce-Codds normalform, BCNF, förbjuder dessa, och är alltså ett hårdare villkor än 3NF. Den förhindrar vissa problem som kan förekomma i 3NF. De tabeller som vi nu skapat, och som är i 3NF, uppfyller faktiskt också kraven för BCNF. Om man designar en databas som är i 3NF, så kommer den oftast att också vara i BCNF.

Den stora fördelen med BCNF är nog att definitionen är enkel:

Annorlunda uttryckt: Rita upp alla fullständiga funktionella beroenden som pilar. Nu ska alla pilar gå från kandidatnycklar. Om du hittar en pil som går från något annat än en kandidatnyckel, så är tabellen inte i BCNF.

Här är ett exempel på en tabell (tabell 14) som är i 3NF men inte i BCNF. Tabellen används för att lagra längden på svenska gator. Gatunamn är unika i varje stad, men inte i hela Sverige. Det kan alltså inte finnas två Storgatan i Gnesta, men det kan finnas en i Gnesta och en i Linköping. Det kan finnas flera postnummerområden i en ort.

Gatunamn Postnummer Ortsnamn Längd
Rydsvägen 58248 Linköping 19 km
Mårdtorpsgatan 58248 Linköping 700 m
Storgatan 58223 Linköping 1500 m
Storgatan 64631 Gnesta 14 m

Det finns två kandidatnycklar, som båda består av två attribut. Den ena är Gatunamn och Postnummer. Den andra är Gatunamn och Ortsnamn.

Tabellen innehåller följande fullständiga funktionella beroenden:

Det finns alltså ett attribut, Postnummer, som bestämmer ett nyckelattribut, Ortsnamn. Det är tillåtet i 3NF. Men som vi ser finns det redundans i tabellen: det står på två ställen att postnummer 58248 finns i Linköping. Dessutom kan man inte lagra data om ett postnummerområde utan att samtidigt lagra data om minst en gata.

Dela upp tabellen i två: dels en tabell med gator (tabell 15), dels en tabell med postnummerområden (tabell 16).

Gatunamn Postnummer Längd
Rydsvägen 58248 19 km
Mårdtorpsgatan 58248 700 m
Storgatan 58223 1500 m
Storgatan 64631 14 m

Postnummer Ortsnamn
58248 Linköping
58223 Linköping
64631 Gnesta

Ännu fler normalformer

Jodå, det finns fler normalformer. Men de är hemliga. Haha!

Ibland är det bra att inte normalisera

Ibland är det bra att inte normalisera. I ett adressregister vill man kanske ha med både postnummer och ort i samma tabell, trots att det egentligen strider mot 3NF. Designen blir förmodligen klarare då (tabell 17):

Nummer Namn Gatuadress Postnummer Ortsnamn
2 Thomas Prästgatan 3D 83131 Östersund
7 Stina Mårdtorpsgatan 7 58248 Linköping
8 Jens Undertorget 1 58248 Linköping

Ett annat skäl till att välja en lägre normaliseringsgrad är prestanda. Om man har höga krav på att sökningar ska gå snabbt, bör man tänka på att det normalt tar längre tid för databashanteraren att söka i flera tabeller än i en enda tabell. När man delat upp en tabell i två, som vi visat tidigare, så måste databashanteraren joina ihop de två tabellerna för att återskapa den ursprungliga tabellen.

Regel: Använd därför teorin med förnuft. Ibland är det bra att inte normalisera. Men om du väljer en lägre normaliseringsgrad bör du ha goda skäl till det, och du bör vara medveten om vilka problem som kan uppstå. När du dokumenterar ditt system bör du sedan ange att du valt en lägre normaliseringsgrad, varför du gjorde det, och vilka problem du förutsett.

Inte bara för relationsmodellen

Normalisering är inte bara möjlig i relationsmodellen, utan även för objektorienterade modeller, eller för poster, eller för entitetstyperna i ett ER-diagram.

De viktigaste begreppen

De viktigaste begreppen från det här avsnittet finns också med i ordlistan:

normalform, normalisering, redundans, första normalformen (1NF), andra normalformen (2NF), tredje normalformen (3NF), Boyce-Codds normalform (BCNF), funktionellt beroende (fb), fullständigt funktionellt beroende (ffb)

Litteratur


Webbkursen om databaser av Thomas Padron-McCarthy.