Lær prinsippene for god databasedesign gjennom de tre normalformene.
Normalisering og databasedesign
Forestill deg at en skole lagrer all informasjon i én eneste tabell:
| elev_id | fornavn | etternavn | klasse | lærer | fag | karakter |
|---|---|---|---|---|---|---|
| 1 | Emma | Hansen | 10A | Kari Nordli | Matematikk | 5 |
| 1 | Emma | Hansen | 10A | Per Haugen | Norsk | 4 |
| 1 | Emma | Hansen | 10A | Lise Vik | Naturfag | 5 |
| 2 | Oliver | Johansen | 10A | Kari Nordli | Matematikk | 3 |
| 2 | Oliver | Johansen | 10A | Per Haugen | Norsk | 4 |
Ser du problemene? Emmas navn og klasse gjentas for hver karakter. Hvis Emma bytter klasse, må vi oppdatere tre rader. Hva hvis vi glemmer én? Da har vi inkonsistent data. Og hva hvis vi sletter alle karakterene til en elev – forsvinner all informasjon om eleven?
Normalisering er løsningen. Det er en systematisk metode for å organisere databasetabeller slik at de unngår disse problemene. I dette kapittelet skal du lære de tre første normalformene og hvordan du bruker dem til å designe gode databaser.
Unormaliserte tabeller (som den store enkelttabellen over) fører til tre typer problemer kalt anomalier:
1. Oppdateringsanomali
Når data er duplisert og du endrer bare noen forekomster, blir dataene inkonsistente.
Eksempel: Emma Hansen bytter fra 10A til 10B. Informasjonen «10A» finnes i tre rader. Hvis vi bare oppdaterer to av dem, står det 10B i to rader og 10A i én rad – inkonsistens.
2. Innsettingsanomali
Du kan ikke lagre informasjon uten å ha tilhørende data.
Eksempel: Vi ansetter en ny lærer, Jon Hauge, som skal undervise i historie. Men vi kan ikke legge ham inn i tabellen uten å også ha en elev og en karakter, fordi alle kolonnene er i samme tabell.
3. Slettingsanomali
Sletting av data fører til utilsiktet tap av annen informasjon.
Eksempel: Hvis vi sletter alle karakterrader for Liam (den eneste eleven i 10C med naturfagskarakter), mister vi ikke bare karakteren, men potensielt informasjonen om at Lise Vik underviser i naturfag.
Alle tre anomaliene skyldes det samme grunnproblemet: data som logisk hører til forskjellige ting er blandet sammen i én tabell. Normalisering løser dette ved å dele data inn i separate, spesialiserte tabeller.
En tabell er i første normalform (1NF) når:
1. Alle kolonner inneholder atomære verdier (udelelige enkelverdier)
2. Det finnes ingen gjentakende grupper eller kolonner
3. Hver rad er unik (tabellen har en primærnøkkel)
Eksempel: Brudd med 1NF
| elevid | fornavn | fag |
|---|---|---|
| 1 | Emma | Matematikk, Norsk, Naturfag |
| 2 | Oliver | Matematikk, Norsk |
Kolonnen
fag inneholder flere verdier i én celle – det bryter med 1NF. Hvordan finner du alle elever som tar naturfag? Du kan ikke bruke WHERE fag = 'Naturfag' fordi verdien er «Matematikk, Norsk, Naturfag».Etter 1NF
| elevid | fornavn | fag |
|---|---|---|
| 1 | Emma | Matematikk |
| 1 | Emma | Norsk |
| 1 | Emma | Naturfag |
| 2 | Oliver | Matematikk |
| 2 | Oliver | Norsk |
Nå har hver celle bare én verdi, og vi kan enkelt søke:
SELECT fornavn FROM elever_fag WHERE fag = 'Naturfag';Et annet eksempel: Gjentakende kolonner
| elev_id | fornavn | fag1 | fag2 | fag3 |
|---|---|---|---|---|
| 1 | Emma | Matematikk | Norsk | Naturfag |
| 2 | Oliver | Matematikk | Norsk | NULL |
Dette bryter også med 1NF – gjentakende kolonner for fag. Hva hvis en elev tar fire fag? Da må vi legge til en ny kolonne. Løsningen er å dele opp i rader, som vist over.
En tabell er i andre normalform (2NF) når:
1. Den er i 1NF
2. Alle ikke-nøkkelkolonner er fullt funksjonelt avhengige av hele primærnøkkelen
2NF er relevant for tabeller med sammensatte primærnøkler (primærnøkler som består av to eller flere kolonner). Hvis en kolonne bare avhenger av én del av nøkkelen, bør den flyttes til en egen tabell.
Eksempel: Brudd med 2NF
Tenk deg en tabell for kursregistrering med sammensatt primærnøkkel (elevid + fagid):
| elevid | fagid | elevnavn | fagnavn | karakter |
|---|---|---|---|---|
| 1 | 1 | Emma | Matematikk | 5 |
| 1 | 2 | Emma | Norsk | 4 |
| 2 | 1 | Oliver | Matematikk | 3 |
Primærnøkkel: (elevid, fagid)
Problem:
elevnavn avhenger bare av elev_id (ikke av hele nøkkelen). fagnavn avhenger bare av fag_id. Bare karakter avhenger av hele nøkkelen (elevid + fagid).Etter 2NF – del opp i tre tabeller:
elever:
| elevid | elevnavn |
|---|---|
| 1 | Emma |
| 2 | Oliver |
fag:
| fagid | fagnavn |
|---|---|
| 1 | Matematikk |
| 2 | Norsk |
karakterer:
| elevid | fagid | karakter |
|---|---|---|
| 1 | 1 | 5 |
| 1 | 2 | 4 |
| 2 | 1 | 3 |
Nå avhenger alle kolonner av hele primærnøkkelen i sin respektive tabell. Elevnavn lagres bare én gang, og fagnavn lagres bare én gang.
En tabell er i tredje normalform (3NF) når:
1. Den er i 2NF
2. Ingen ikke-nøkkelkolonne er avhengig av en annen ikke-nøkkelkolonne (ingen transitive avhengigheter)
Med andre ord: alle ikke-nøkkelkolonner avhenger direkte av primærnøkkelen, ikke indirekte via andre kolonner.
Eksempel: Brudd med 3NF
| elevid | fornavn | etternavn | postnummer | poststed |
|---|---|---|---|---|
| 1 | Emma | Hansen | 0150 | Oslo |
| 2 | Oliver | Johansen | 0150 | Oslo |
| 3 | Nora | Olsen | 5003 | Bergen |
Problem:
poststed avhenger av postnummer, som igjen avhenger av elev_id. Dette er en transitiv avhengighet: elevid → postnummer → poststed. Poststed avhenger ikke direkte av elevid.Konsekvensen er at «Oslo» lagres to ganger (for postnummer 0150). Hvis postnummer 0150 bytter navn, må vi oppdatere alle rader.
Etter 3NF – skill ut poststeder:
elever:
| elevid | fornavn | etternavn | postnummer |
|---|---|---|---|
| 1 | Emma | Hansen | 0150 |
| 2 | Oliver | Johansen | 0150 |
| 3 | Nora | Olsen | 5003 |
poststeder:
| postnummer | poststed |
|---|---|
| 0150 | Oslo |
| 5003 | Bergen |
Nå lagres hvert poststed bare én gang, og alle ikke-nøkkelkolonner avhenger direkte av primærnøkkelen i sin respektive tabell.
2NF – Full funksjonell avhengighet: Alle ikke-nøkkelkolonner avhenger av hele primærnøkkelen, ikke bare en del av den. (Relevant ved sammensatte primærnøkler.)
3NF – Ingen transitive avhengigheter: Alle ikke-nøkkelkolonner avhenger direkte av primærnøkkelen, ikke av andre ikke-nøkkelkolonner.
Huskeregel: «Nøkkelen, hele nøkkelen, og ingenting annet enn nøkkelen.»
- 1NF: Dataene har en nøkkel (primærnøkkel, atomære verdier)
- 2NF: Avhenger av hele nøkkelen (ikke deler av sammensatt nøkkel)
- 3NF: Ingenting annet enn nøkkelen (ingen transitive avhengigheter)
| utlaan_id | elevnavn | klasse | boktittel | forfatter | isbn | utdato | inndato |
|---|---|---|---|---|---|---|---|
| 1 | Emma Hansen | 10A | Sofies verden | Jostein Gaarder | 978-82-03-19 | 2024-09-01 | 2024-09-15 |
| 2 | Emma Hansen | 10A | Beatles | Lars S. Christensen | 978-82-02-25 | 2024-09-10 | NULL |
| 3 | Oliver Johansen | 10A | Sofies verden | Jostein Gaarder | 978-82-03-19 | 2024-09-05 | 2024-09-20 |
Problemer:
- Emmas navn og klasse gjentas (redundans)
- «Sofies verden» med forfatter og ISBN gjentas (redundans)
-
elevnavn er ikke atomært (fornavn + etternavn i én kolonne) → bryter 1NFSteg 1: 1NF – atomære verdier
Del elevnavn i fornavn og etternavn. Fjern gjentakende data.
Steg 2: 2NF og 3NF – fjern partielle og transitive avhengigheter
CREATE TABLE elever (
elev_id INTEGER PRIMARY KEY AUTOINCREMENT,
fornavn TEXT NOT NULL,
etternavn TEXT NOT NULL,
klasse TEXT
);
CREATE TABLE boker (
bok_id INTEGER PRIMARY KEY AUTOINCREMENT,
tittel TEXT NOT NULL,
forfatter TEXT,
isbn TEXT UNIQUE
);
CREATE TABLE utlaan (
utlaan_id INTEGER PRIMARY KEY AUTOINCREMENT,
elev_id INTEGER NOT NULL,
bok_id INTEGER NOT NULL,
utlaans_dato DATE NOT NULL,
innleverings_dato DATE,
FOREIGN KEY (elev_id) REFERENCES elever(elev_id),
FOREIGN KEY (bok_id) REFERENCES boker(bok_id)
);
-- Sett inn data
INSERT INTO elever (fornavn, etternavn, klasse) VALUES
('Emma', 'Hansen', '10A'),
('Oliver', 'Johansen', '10A');
INSERT INTO boker (tittel, forfatter, isbn) VALUES
('Sofies verden', 'Jostein Gaarder', '978-82-03-19'),
('Beatles', 'Lars S. Christensen', '978-82-02-25');
INSERT INTO utlaan (elev_id, bok_id, utlaans_dato, innleverings_dato) VALUES
(1, 1, '2024-09-01', '2024-09-15'),
(1, 2, '2024-09-10', NULL),
(2, 1, '2024-09-05', '2024-09-20');Nå er data normalisert: elevinformasjon lagres bare én gang, bokinformasjon lagres bare én gang, og utlånstabellen kobler dem sammen.
Normalisering er generelt en god praksis, men det finnes situasjoner der man bevisst velger å denormalisere – altså tillate noe redundans:
Ytelseshensyn
Normaliserte databaser krever mange JOIN-operasjoner for å sette sammen data fra flere tabeller. I systemer med ekstremt mange spørringer (som sosiale medier med millioner av brukere) kan dette bli en flaskehals. Da kan man bevisst duplisere noe data for å unngå dyre JOIN-operasjoner.
Rapportering og analyse
For rapporterings-databaser (datawarehouses) er det vanlig å bruke «flate» tabeller med noe redundans, fordi det gjør spørringene enklere og raskere.
Enkle systemer
For svært enkle systemer med lite data kan full normalisering gjøre ting unødvendig komplisert.
Hovedregelen er: Start alltid med normalisering. Denormaliser bare hvis du har dokumenterte ytelsesproblemer som ikke kan løses på andre måter. Det er mye lettere å denormalisere en normalisert database enn å normalisere en rotete en.
Når du skal designe en database fra bunnen av, følg denne prosessen:
1. Forstå kravene – Hva skal systemet gjøre? Hvilke data trengs?
2. Identifiser entiteter – Hva er «tingene» vi lagrer? (Elever, bøker, bestillinger...)
3. Definer attributter – Hvilke egenskaper har hver entitet?
4. Finn relasjoner – Hvordan henger entitetene sammen?
5. Tegn ER-diagram – Visualiser strukturen
6. Normaliser – Sjekk 1NF, 2NF, 3NF og juster tabellene
7. Skriv SQL – Opprett tabellene med CREATE TABLE
8. Test med data – Sett inn testdata og kjør spørringer
9. Iterer – Juster designet basert på reelle behov
God databasedesign er en ferdighet som utvikles med praksis. Jo flere databaser du designer, jo bedre intuisjon utvikler du for hva som fungerer og hva som ikke fungerer.
Pass deg for disse vanlige feilene:
1. Kommaseparerte lister i kolonner: fag = "Matte, Norsk, Naturfag" bryter med 1NF. Bruk en koblingstabell.
2. Gjentakende kolonner: telefon1, telefon2, telefon3 bryter med 1NF. Lag en egen tabell for telefonnumre.
3. Beregnede verdier lagret i tabellen: Ikke lagre alder hvis du har fodselsdato. Beregn det med SQL i stedet.
4. Miks av ulike entiteter: Ikke legg elevinfo og faginfo i samme tabell bare fordi de brukes sammen.
5. For mye normalisering: Det finnes tilfeller der det er greit å lagre noe «ekstra» data for å forenkle spørringer.
Hva er normalisering i databasesammenheng?
Denne tabellen bryter med første normalform (1NF). Hvorfor?
| elev_id | fornavn | hobbyer |
|---|---|---|
| 1 | Emma | Fotball, Lesing, Sjakk |
| 2 | Oliver | Gaming, Fotball |
Hvilken type anomali oppstår i dette scenariet?
Du har en tabell der elevnavn og klasse lagres sammen med karakterer. Emma Hansen er i klasse 10A og har tre karakterrader. Du oppdaterer klassen til 10B i to av radene, men glemmer den tredje.
Denne tabellen er unormalisert. Identifiser hvilke normalformer den bryter med, og vis hvordan du normaliserer den til 3NF.
| bestillingid | kundenavn | kundeepost | produkt | pris | antall | totalpris |
|---|---|---|---|---|---|---|
| 1 | Emma Hansen | emma@mail.no | USB-kabel | 79 | 2 | 158 |
| 2 | Emma Hansen | emma@mail.no | Mus | 299 | 1 | 299 |
| 3 | Oliver Johansen | oliver@mail.no | USB-kabel | 79 | 3 | 237 |
Hvilken normalform bryter denne tabellen med?
| ansattid | navn | avdeling | avdelingsleder |
|---|---|---|---|
| 1 | Kari Nordli | Matematikk | Per Haugen |
| 2 | Lise Vik | Naturfag | Tom Bakke |
| 3 | Jon Hauge | Matematikk | Per Haugen |
Primærnøkkel: ansattid. Avdelingsleder avhenger av avdeling (ikke direkte av ansatt_id).
Design en fullstendig normalisert database (3NF) for et treningssenter. Systemet skal håndtere:
- Medlemmer (navn, telefon, e-post, medlemstype)
- Treningsklasser (navn, beskrivelse, varighet, instruktør)
- Påmeldinger til klasser (medlem, klasse, dato, tidspunkt)
- Instruktører (navn, spesialisering, telefon)
Tegn eller beskriv tabellene med kolonner, primærnøkler, fremmednøkler og relasjonstyper. Skriv deretter CREATE TABLE-kode for alle tabellene.
Hva er huskeregelen for de tre første normalformene?
Oppsummering
I dette kapittelet har du lært:
- Normalisering: organiserer data for å unngå redundans.
- Anomalier: oppdaterings-, innsettings- og slettingsanomali.
- 1NF: atomaere verdier, ingen gjentakende grupper.
- 2NF: ingen delvis avhengighet av sammensatt nøkkel.
- 3NF: ingen transitiv avhengighet.
Noekkelbegreper
| Begrep | Forklaring |
|---|---|
| Normalisering | Å organisere data for å minimere redundans |
| 1NF | Atomaere verdier i hver kolonne |
| 3NF | Ingen transitiv avhengighet |
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.