ER → relasjonsskjema
Mekaniske regler for å oversette diagram til tabeller — og hvor anomalien sniker seg inn hvis man slurver.
Diagram til tabeller
Et godt ER-diagram inneholder all informasjonen vi trenger for å lage et relasjonsskjema. Reglene er mekaniske: gjør X med entiteter, gjør Y med relasjoner, gjør Z med svake entiteter. Du skal ikke tenke kreativt under oversettelsen — den kreative delen var å lage diagrammet i første runde.
Lossless — ingen informasjon tapes. Constraint-bevarende — primær- og fremmednøkler reflekterer kardinalitet og deltakelse. Effektiv — så få tabeller som mulig uten å miste det første.
Den korte oppskriften
- Hver sterk entitet → tabell.
- Hver svak entitet → tabell med sammensatt nøkkel som arves fra eieren.
- Hver fler-verdig attributt → egen tabell.
- Hver relasjon → fremmednøkkel-kolonne (1:1, 1:N) eller egen tabell (N:M).
- ISA-hierarkier → en av tre strategier (vi kommer til det).
Entitet blir tabell
For en sterk entitet E med atomære attributter a₁, a₂, …, aₙ der a₁ er nøkkel:
CREATE TABLE E (
a1 PRIMARY KEY,
a2 …,
…
an …
);
Eksempel: Student med (studnr, navn, fødselsdato) blir:
CREATE TABLE Student (
studnr INT PRIMARY KEY,
navn VARCHAR(100) NOT NULL,
fodselsdato DATE
);
Kolonnenavn på engelsk eller norsk uten æøå er typisk i industrien for portabilitet. Lærebokas eksempler bruker engelsk, men på eksamen er norsk akseptabelt.
Når et attributt ikke er atomært
Sammensatt attributt → flate kolonner
adresse(gate, postnr, sted) blir til tre flate kolonner:
CREATE TABLE Person (
pnr CHAR(11) PRIMARY KEY,
adresse_gate VARCHAR(120),
adresse_postnr CHAR(4),
adresse_sted VARCHAR(60)
);
Fler-verdig attributt → egen tabell
Et attributt som tlf-numre kan ikke representeres som én kolonne (det ville bryte 1NF — atomaritet). Vi lager en egen tabell der nøkkelen er sammensatt av eier-entitetens nøkkel og attributtverdien:
CREATE TABLE Person_tlf (
pnr CHAR(11),
tlf VARCHAR(20),
PRIMARY KEY (pnr, tlf),
FOREIGN KEY (pnr) REFERENCES Person(pnr) ON DELETE CASCADE
);
Komma-separerte verdier i én kolonne er klassisk anti-pattern. Det ødelegger indekser, sortering, joins og constraint-håndhevelse. Hver gang du fristes: tenk at databasen ble laget i 1970 nettopp for å unngå dette.
kompetanse på Ansatt(ansattnr, …)?(ansattnr, kompetanse). Slik unngår man at samme kompetanse registreres dobbelt på samme person, samtidig som flere personer kan ha samme kompetanse og én person kan ha mange.Relasjon → kolonne eller tabell
Beslutningsregelen
- 1:1 — fremmednøkkel i én av tabellene. Velg den siden med total deltakelse (unngår nullverdier).
- 1:N — fremmednøkkel på N-siden. Den ene siden av relasjonen får en kolonne som peker til den andre.
- N:M — egen tabell med to fremmednøkler som sammen utgjør primærnøkkelen.
1:N-eksempel: Avdeling har Ansatte
Ansatt-tabellen får en fremmednøkkel avd_id til Avdeling:
CREATE TABLE Ansatt (
ansattnr INT PRIMARY KEY,
navn VARCHAR(80),
avd_id INT NOT NULL, -- total deltakelse
FOREIGN KEY (avd_id) REFERENCES Avdeling(avd_id)
);
Hadde Avdeling fått en kolonne ansatt_id, kunne kolonnen bare peke på én ansatt — men avdelinger har flere. Plassér FK på siden hvor det er maks én peker. Det er alltid N-siden i en 1:N.
Person og Pass der hver person har ett pass og hvert pass har én eier — hvor plasseres fremmednøkkelen?Pass.eier_pnr er den naturlige plasseringen.Identitet arves
Svake entiteter blir egne tabeller, men den primære nøkkelen er sammensatt: eierens nøkkel + diskriminatoren. Eierens nøkkel er samtidig fremmednøkkel.
CREATE TABLE Ordrelinje (
ordrenr INT,
linjenr INT,
vare_id INT NOT NULL,
antall INT NOT NULL,
PRIMARY KEY (ordrenr, linjenr),
FOREIGN KEY (ordrenr) REFERENCES Ordre(ordrenr) ON DELETE CASCADE,
FOREIGN KEY (vare_id) REFERENCES Vare(vare_id)
);
Slettingsregelen følger semantikken: en svak entitet kan ikke eksistere uten eieren. Mister du ordren, slettes alle linjene automatisk. Det er konseptuelt riktig, og fjerner muligheten for «forelderløse» rader.
Ordre — har — Ordrelinje når vi reduserer en svak entitet?ordrenr-kolonnen er fremmednøkkelen, og samtidig en del av primærnøkkelen. En egen relasjonstabell ville være redundant — den ville bare kopiere allerede eksisterende informasjon.Tre alternativer for spesialisering
Det finnes ikke ett riktig svar — strategi velges ut fra hvor mange unike attributter subklassene har, og hvilke spørringer som er vanlige.
NULL for rader av annen type.Bra når: få spesialiserte attributter, mye polymorfe spørringer. Dårlig når: mange unike kolonner per subtype → mange null-celler.
Bra når: total + disjoint, og du sjelden spør på tvers. Dårlig når: mye polymorfe spørringer (UNION ALL kreves).
Bra når: mange spesialiserte attributter, klar disjoint-struktur. Dårlig når: du leser på tvers ofte (krever join).
pnr er både PK i subtabellen og FK til supertabellen.Bok, Tidsskrift og DVD. Felles: katalognr, tittel, hyllenr. Bok har 50 attributter unike for bok; DVD har 3. Hvilken strategi er typisk best?Universitet → tabeller, helt
La oss kjøre gjennom et lite, men realistisk eksempel. Krav:
- En
Avdelinghar et navn (unikt) og en bygning. Avdelingen kan eksistere uten ansatte. - En
Lærerjobber i nøyaktig én avdeling. Lærere har lønn, navn, ansattnummer. - Hver lærer kan ha 0 eller flere telefonnumre.
- Lærere underviser
Emner. Et emne kan undervises av flere lærere; en lærer kan undervise flere emner. Per (lærer, emne)-par registreres et semester. Pårørendetil en lærer er en svak entitet. Identifiseres ved (lærer, navn). Vi lagrer relasjonstype.
Resulterende skjema
CREATE TABLE Avdeling (
avd_navn VARCHAR(60) PRIMARY KEY,
bygning VARCHAR(40)
);
CREATE TABLE Larer (
ansattnr INT PRIMARY KEY,
navn VARCHAR(80) NOT NULL,
lonn DECIMAL(10,2),
avd_navn VARCHAR(60) NOT NULL, -- total deltakelse
FOREIGN KEY (avd_navn) REFERENCES Avdeling(avd_navn)
);
CREATE TABLE Larer_tlf ( -- fler-verdig attributt
ansattnr INT,
tlf VARCHAR(20),
PRIMARY KEY (ansattnr, tlf),
FOREIGN KEY (ansattnr) REFERENCES Larer(ansattnr) ON DELETE CASCADE
);
CREATE TABLE Emne (
emnekode CHAR(7) PRIMARY KEY,
tittel VARCHAR(100)
);
CREATE TABLE Underviser ( -- N:M-relasjon med attributt
ansattnr INT,
emnekode CHAR(7),
semester CHAR(5), -- 'V2026' f.eks.
PRIMARY KEY (ansattnr, emnekode, semester),
FOREIGN KEY (ansattnr) REFERENCES Larer(ansattnr),
FOREIGN KEY (emnekode) REFERENCES Emne(emnekode)
);
CREATE TABLE Parorende ( -- svak entitet
ansattnr INT,
pnavn VARCHAR(80),
relasjon VARCHAR(30),
PRIMARY KEY (ansattnr, pnavn),
FOREIGN KEY (ansattnr) REFERENCES Larer(ansattnr) ON DELETE CASCADE
);
Sterke entiteter (Avdeling, Lærer, Emne) → egne tabeller. 1:N (Avdeling–Lærer) → FK på N-siden. Fler-verdig attributt → egen tabell. N:M med attributt → mellomtabell. Svak entitet → sammensatt PK + ON DELETE CASCADE. Total deltakelse → NOT NULL på FK.
Tre ansikter til anomali
Selv etter mekanisk reduksjon kan skjemaet ha problemer — typisk fordi ER-diagrammet har slått sammen entiteter som burde vært skilt. Tenk deg dette dårlige skjemaet:
InDep(id, navn, lonn, avd_navn, bygning, budsjett)
Vi har slått sammen Lærer og Avdeling. Hver rad gjentar bygning og budsjett for hver lærer i samme avdeling. La oss se hva som skjer:
| id | navn | lønn | avd_navn | bygning | budsjett |
|---|---|---|---|---|---|
| 10101 | Srinivasan | 65 000 | Comp.Sci. | Taylor | 100 000 |
| 45565 | Katz | 75 000 | Comp.Sci. | Taylor | 100 000 |
| 83821 | Brandt | 92 000 | Comp.Sci. | Taylor | 100 000 |
| 22222 | Einstein | 95 000 | Physics | Watson | 70 000 |
| 33456 | Gold | 87 000 | Physics | Watson | 70 000 |
NULL for id, navn, lønn) — for tabellen krever at hver rad har lærer-info.(Physics, Watson, 70 000) — selv om Physics ennå eksisterer som avdeling.Når samme faktum dukker opp gjentatt på flere rader, er det et tegn på at to konseptuelt forskjellige ting deler samme tabell. Løsningen er dekomponering — splitt i to tabeller og knytt dem med fremmednøkkel.
Dekomponert versjon
| id | navn | lønn | avd_navn |
|---|---|---|---|
| 10101 | Srinivasan | 65 000 | Comp.Sci. |
| 45565 | Katz | 75 000 | Comp.Sci. |
| 22222 | Einstein | 95 000 | Physics |
| avd_navn | bygning | budsjett |
|---|---|---|
| Comp.Sci. | Taylor | 100 000 |
| Physics | Watson | 70 000 |
| Music | Packard | 80 000 |
Hver avdeling har nøyaktig én rad med sitt budsjett. Ny avdeling registreres uten lærere. Sletter vi den siste Physics-læreren, ligger Physics fremdeles trygt i Avdeling-tabellen.
Mekanisk diagnose
Anomalier er ikke tilfeldigheter. De er symptomer på spesifikke mønstre i avhengighetene mellom attributtene. Hvis vi kan beskrive avhengighetene formelt, kan vi bygge en algoritme som oppdager problemene før dataen kommer inn — og som forteller oss hvilken dekomponering som løser dem.
Det formelle språket heter funksjonelle avhengigheter (FD). En FD X → Y sier «hvis du vet X, kan du regne ut Y». I InDep-eksempelet:
id → navn, lonn, avd_navn— lærer-id bestemmer alt om lærerenavd_navn → bygning, budsjett— avdelingsnavnet bestemmer alt om avdelingen
Når den andre FD-en holder uten at avd_navn er supernøkkel for hele tabellen, har vi per definisjon et BCNF-brudd. Algoritmen for å splitte er mekanisk: «for hver FD α → β der α ikke er supernøkkel, splitt i (α ∪ β) og (R − (β − α))».
Det er i 4C — neste underkapittel — at vi setter formaliteten på papir: hva en FD er, hvordan vi beregner lukninger, hva som er forskjellen mellom 1NF, 2NF, 3NF og BCNF, og når vi taper dependency preservation.
Gå videre til 4C → normalformer og lær det matematiske språket bak alt vi har observert i dette underkapittelet.