Placér data, hvor de hører hjemme
Findes også på 28 sprog
English · Español · 日本語 · Português (Brasil) · Français · Deutsch · Italiano · 한국어 · 简体中文 · Português (Portugal) · 繁體中文 · Bahasa Indonesia · Türkçe · Polski · Tiếng Việt · ไทย · Nederlands · العربية · עברית · Українська · हिन्दी · Русский · Svenska · Norsk bokmål · Suomi · Español (México) · Français (Canada) · Deutsch (Schweiz)

Modul 4 af 6 i kapitlet Modellér data for betydning, kapitel 2 i kurset Data Architect. Hvert modul står på egne ben. Forrige: Byg den logiske model · Næste: Design til tredje normalform, og træd så et skridt tilbage
Spørgsmålet, dette modul besvarer: Hvordan hjælper normalisering mig med at finde ud af, hvor data virkelig hører hjemme?
Vi har en logisk model: entiteter, relationer, attributter og nøgler. Nu vil jeg stille et af de vigtigste spørgsmål i datamodellering: hører de her data faktisk hjemme her?
Det lyder enkelt. Det er det ikke. Jeg har set masser af systemer, hvor hvert eneste felt gav mening, da nogen oprettede det. Problemet var ikke, at felterne var latterlige; det var, at kendsgerningerne var gemt det forkerte sted. En levering indeholder information, der i virkeligheden beskriver et distributionscenter. Den samme adresse står på hundredvis af poster. Ét felt indeholder tre værdier adskilt af kommaer. En ændring af én forretningskendsgerning betyder, at 10.000 poster skal opdateres. Det er det problem, normalisering hjælper os med at løse, og før vi bliver begravet i databaseterminologi, er her hele idéen: placér en kendsgerning, hvor den hører hjemme. Alt andet i dette modul hjælper os med at gøre det mere systematisk.
Hvad er normalisering?
Sektion kaldt “Hvad er normalisering? ”Normalisering er at organisere data, så hver kendsgerning gemmes én gang, på det sted, hvor den hører hjemme. Når én tabel udfører for mange job, deler vi den op i tabeller, der hver beskriver én ting, og vi forbinder dem med nøgler.
Hvorfor gøre sig umaget? Fordi en kendsgerning, der er gemt én gang, er en kendsgerning, vi ændrer én gang. Normalisering forhindrer redundans, den samme kendsgerning kopieret ud på mange poster, og dermed de uoverensstemmelser, der sniger sig ind, når én kopi bliver opdateret og de andre ikke gør. Derfor passer det til transaktionssystemer som Salesforce, hvor poster bliver oprettet og opdateret hele dagen.
Vi normaliserer i trin, der kaldes normalformer, og hvert trin stiller ét spørgsmål:
- Første normalform (1NF): én værdi pr. felt og noget, der entydigt identificerer hver række.
- Anden normalform (2NF): hver kendsgerning beskriver hele det, der identificerer rækken, ikke kun en del af det.
- Tredje normalform (3NF): hver kendsgerning beskriver denne ting, ikke noget andet, den hænger sammen med.
Der findes flere former ud over den tredje. Jeg siger et par ord om dem senere, og om hvorfor vi som regel stopper ved den tredje. Lad os først se de tre trin ske.
Hvorfor normaliserer vi?
Sektion kaldt “Hvorfor normaliserer vi? ”Når jeg underviser i det her live, starter jeg ikke med leveringer. Jeg starter med hunde. Alle forstår hunde, så ingen bliver distraheret af forretningen, og vi kan se dataene prøve at finde deres hjem. Så tager vi de samme greb med over til Steve Trucking.
Sit, bliv, modellér
Sektion kaldt “Sit, bliv, modellér ”Start: det rå regneark. Jeg sætter et par hunde op på skærmen, og så tilføjer rummet deres egne. Inden for få minutter har vi én stor liste: hunden, året den blev født, hvem der ejer den, hvor ejeren bor, racen, hvem der har opdrættet den, og de tricks, den kan.
| Hund | Født | Ejer | Postnr. | By | Race | Opdrætter | Tricks |
|---|---|---|---|---|---|---|---|
| Bella | 2017 | Marcus | 80202 | Denver | Labrador | Maple Ridge | Sit, Bliv, Giv pote |
| Milo | 2019 | Marcus | 80202 | Denver | Puddel | Golden Meadow | Sit, Rul rundt |
| Max | 2022 | Ana | 64105 | Kansas City | Blanding | Sit, Død hund | |
| Luna | 2020 | Kenji | 98101 | Seattle | Husky | Cascade | Sit, Bliv, Snurre rundt |
| Rocky | 2024 | Priya | 30301 | Atlanta | Beagle | Maple Ridge | Sit |
| Coco | 2014 | Ana | 64105 | Kansas City | Chihuahua | Little Paws | Giv pote, Snurre rundt |
Det virker. Ethvert regneark virker på dag ét. Men se, hvad der allerede er galt. Hvordan finder jeg alle hunde, der kan Giv pote? Anas postnummer står på to rækker, og det samme gør Marcus’: hvad sker der, hvis nogen opdaterer det ene og ikke det andet? Maple Ridge optræder to gange, og det samme gør Kansas City. Og Max kom fra internatet, så der er slet ingen opdrætter.
Trin 1: første normalform. To regler. Hvert felt indeholder én enkelt værdi, så kommalisten med tricks skal væk. Og hver række har brug for noget, der entydigt identificerer den. Ikke hundens navn; der er mange hunde, der hedder Max. Et id: et automatisk genereret id eller et GUID. Tricksene kommer ud i deres egne rækker, én række for hver hund og hvert trick. Og når et trick først har sin egen række, er der plads til det, vi aldrig kunne sige i en kommaliste: hvor svært tricket er, hvor godt denne hund gør det, og hvornår den lærte det.
Hund (identificeret ved hunde-id)
| Hunde-id | Navn | Født | Ejer | Postnr. | By | Race | Opdrætter |
|---|---|---|---|---|---|---|---|
| D-001 | Bella | 2017 | Marcus | 80202 | Denver | Labrador | Maple Ridge |
| D-002 | Milo | 2019 | Marcus | 80202 | Denver | Puddel | Golden Meadow |
| D-003 | Max | 2022 | Ana | 64105 | Kansas City | Blanding | |
| D-004 | Luna | 2020 | Kenji | 98101 | Seattle | Husky | Cascade |
| D-005 | Rocky | 2024 | Priya | 30301 | Atlanta | Beagle | Maple Ridge |
| D-006 | Coco | 2014 | Ana | 64105 | Kansas City | Chihuahua | Little Paws |
Hundetrick (identificeret ved hunde-id + trick)
| Hunde-id | Trick | Sværhedsgrad | Færdighedsniveau | Lært |
|---|---|---|---|---|
| D-001 | Sit | Let | Ekspert | 2017-11 |
| D-001 | Bliv | Let | Ekspert | 2018-01 |
| D-001 | Giv pote | Middel | God | 2018-06 |
| D-002 | Sit | Let | Ekspert | 2020-02 |
| D-002 | Rul rundt | Svær | Lærer det | 2023-05 |
| D-003 | Sit | Let | God | 2022-06 |
| D-003 | Død hund | Svær | Lærer det | 2024-03 |
| D-004 | Sit | Let | Ekspert | 2020-11 |
| D-004 | Bliv | Let | God | 2021-01 |
| D-004 | Snurre rundt | Middel | Ekspert | 2021-06 |
| D-005 | Sit | Let | Lærer det | 2025-02 |
| D-006 | Giv pote | Middel | Ekspert | 2015-06 |
| D-006 | Snurre rundt | Middel | Lærer det | 2025-04 |
Nu er »alle hunde, der kan Giv pote« et enkelt spørgsmål. Men læg mærke til, hvad Hundetrick identificeres ved: ikke én ting, men to ting sammen, hunden og tricket. Hold fast i det.
Trin 2: anden normalform. Anden normalform betyder kun noget, når en række identificeres ved en kombination, og det gør Hundetrick. Så jeg spørger om hver kolonne: beskriver den hele kombinationen, eller kun en del af den? Færdighedsniveau beskriver denne hund, der udfører dette trick: Bella er ekspert i Sit, Rocky er stadig ved at lære det. Lært, det samme. Men sværhedsgrad? Sit er let, uanset hvilken hund der gør det. Det beskriver tricket, kun en del af nøglen, og derfor står »Let« på hver eneste Sit-række. Så tricket får sin egen tabel, og tilbage er parret.
Trick
| Trick-id | Trick | Sværhedsgrad |
|---|---|---|
| T-01 | Sit | Let |
| T-02 | Bliv | Let |
| T-03 | Giv pote | Middel |
| T-04 | Rul rundt | Svær |
| T-05 | Død hund | Svær |
| T-06 | Snurre rundt | Middel |
Hundetrick (hunde-id + trick-id)
| Hunde-id | Trick-id | Færdighedsniveau | Lært |
|---|---|---|---|
| D-001 | T-01 | Ekspert | 2017-11 |
| D-001 | T-02 | Ekspert | 2018-01 |
| D-001 | T-03 | God | 2018-06 |
| D-002 | T-01 | Ekspert | 2020-02 |
| D-002 | T-04 | Lærer det | 2023-05 |
| D-003 | T-01 | God | 2022-06 |
| D-003 | T-05 | Lærer det | 2024-03 |
| D-004 | T-01 | Ekspert | 2020-11 |
| D-004 | T-02 | God | 2021-01 |
| D-004 | T-06 | Ekspert | 2021-06 |
| D-005 | T-01 | Lærer det | 2025-02 |
| D-006 | T-03 | Ekspert | 2015-06 |
| D-006 | T-06 | Lærer det | 2025-04 |
Den Hundetrick-tabel er den, vi fandt i modul 2, og den har et navn i datamodellering: en forbindelse, eller som jeg kalder den, et logisk forbindelsesobjekt. En hund kan mange tricks; et trick kan mange hunde. Forbindelsen sidder mellem dem, og den er ikke tom rørføring: den indeholder de kendsgerninger, der hører til selve relationen, færdighedsniveauet og datoen. Vi møder den igen i modul 6, og i kapitel 3 ser vi, at Salesforce bruger »junction object« snævrere: et forbindelsesobjekt i Salesforce (Salesforce Junction Object) er et brugerdefineret objekt med to master-detail-relationer (kapitel 3, modul 7).
Trin 3: tredje normalform. Nu tilbage til Hund-tabellen. Alt i den identificeres ved hunde-id’et, så anden normalform har intet at sige. Tredje normalform stiller et andet spørgsmål: beskriver denne kendsgerning hunden, eller noget andet, hunden hænger sammen med? Racen er en ting for sig: en labrador er en labrador, uanset hvilken hund det er, og kendsgerninger om den, som dens størrelse, hører til racen, ikke til Bella. Postnummeret og byen beskriver ejeren, ikke hunden: det er der, Ana bor, uanset hvilken af hendes hunde vi kigger på. Opdrætterens oplysninger beskriver opdrætteren. Hver af dem er en kendsgerning om en anden kendsgerning, så hver af dem flytter til sin egen tabel, og hunden peger på den med en fremmednøgle:
- Hund: hunde-id, navn, født, ejer-id, race-id, opdrætter-id
- Ejer: ejer-id, navn, postnr., by
- Race: race-id, race, størrelse
- Opdrætter: opdrætter-id, navn, by
- Trick og Hundetrick fra trin 2
Ana er nu gemt én gang, og begge hendes hunde peger på hende. Maple Ridge er gemt én gang, og Race-tabellen giver os et hjem til kendsgerninger om hver race, som dens størrelse, som regnearket ikke havde noget sted at gøre af. En fremmednøgle kan være tom: Max har intet opdrætter-id, og det er et ærligt svar, ikke et hul. Så kaster jeg et drilsk spørgsmål ud til rummet: Da ejeren fik hunden, hvor skal den dato så hen? På hunden. En hund har én ejer ad gangen i denne model, så den dato beskriver hundens relation til sin ejer.
Du kunne gå et skridt videre og skille byen fra postnummeret; det gør lærebøgerne. I praksis holdes en adresse samlet, og Kansas City ligger i øvrigt i to stater, en god påmindelse om, at den virkelige verden ikke læser vores lærebøger.
Det er forløbet: et regneark, så tre spørgsmål, og hver kendsgerning ender ét sted. Træd et skridt tilbage, og se på, hvad vi har bygget. Én rodet liste blev til seks tabeller, der hver beskriver én ting, forbundet med nøgler:
Ingen havde brug for et eneste Salesforce-ord for at følge med. Det er bare hundene, menneskene omkring dem, og hvad de kan.
Lad os nu bruge det på Steve Trucking
Sektion kaldt “Lad os nu bruge det på Steve Trucking ”Hundene gør grebene nemme at se. En rigtig virksomhed gør dem vigtige, for de samme fejl koster rigtige penge i stor skala. Så lad os tage de samme tre spørgsmål med til Steve Trucking. Forestil dig, at de holder styr på deres leveringer sådan her:
| Levering | Detailpartner | Distributionscenter | DC-by | Team | Lastbil |
|---|---|---|---|---|---|
| 1001 | Keystone | Keystone Phoenix DC | Phoenix | Team 12 | 117 |
| 1002 | Keystone | Keystone Phoenix DC | Phoenix | Team 8 | 122 |
| 1003 | Keystone | Keystone Phoenix DC | Phoenix | Team 12 | 117 |
Ved første øjekast er der intet kompliceret her. Men se på distributionscentret. Vi gentager »Keystone Phoenix DC, Phoenix« på hver levering, der kører derfra: Steve Trucking laver cirka 3.300 leveringer om dagen fordelt på ni DC’er, så et par hundrede om dagen fra hvert. Hvad sker der, hvis det DC flytter? Hvor mange poster skal vi ændre, og hvad sker der, hvis én bliver glemt? Nu siger dataene, at DC’et ligger i to byer på én gang. Det er en opdateringsanomali. Hvad hvis vi vil oprette et nyt DC før dets første levering? Hvad hvis vi sletter den sidste levering fra et DC: har vi så lige slettet det eneste sted, hvor vi gemte noget om det?
Det er forskellige symptomer på det samme problem: vi har blandet kendsgerninger om forskellige ting sammen. Kendsgerninger om distributionscentret hører til distributionscentret. Kendsgerninger om leveringen hører til leveringen. Kendsgerninger om teamet hører til teamet. Det er normalisering i praksis.
Første normalform: gem ikke gentagne strukturer
Sektion kaldt “Første normalform: gem ikke gentagne strukturer ”Lad os starte med første normalform, eller 1NF, hvor folk nogle gange gør normalisering sværere, end den behøver at være.
Forestil dig, at en levering har et felt, der hedder Varer: »Køleskab, ismaskinesæt, bortkørsel.« Vi har lagt tre stykker information i ét felt. Det virker praktisk, indtil nogen spørger: »Hvor mange bortkørsler lavede vi sidste måned?« Eller forestil dig felter, der hedder Vare 1, Vare 2 og Vare 3. Det virker, indtil leveringen med fire varer. Problemet er ikke, at vi valgte det forkerte antal varefelter. Problemet er, at vi ikke genkendte den gentagne struktur. En levering kan have flere varer. Det er data, vi bør modellere: den leveringslinje, vi fandt i modul 2.
Den praktiske lære af 1NF: gem ikke gentagne strukturer inde i felter. Hvis forretningen fortæller dig, at noget kan ske mange gange, så lyt. Der er måske en anden entitet, der prøver at slippe ud.
Det ser du hele tiden i rigtige org’er. Nogen har brug for en beskrivelse på flere sprog, så de opretter et felt, der hedder Description_English, så Description_French, så Description_German. Eller en godkendelse kræver et par personer, så vi får Godkender 1, Godkender 2, Godkender 3. Når jeg finder det i en gennemgang, er det et slag over fingrene: ret din datamodel. Et eller to, måske, hvis forretningen virkelig kun nogensinde har et eller to. Alt ud over det bør være et underordnet objekt: en Oversættelse med et sprog, et Godkendelsestrin med en godkender. Det næste sprog eller den fjerde godkender bliver en ny post, ikke et nyt felt og en deployment.
Anden normalform: beskriv hele tingen
Sektion kaldt “Anden normalform: beskriv hele tingen ”Anden normalform, eller 2NF, betyder noget, når en post identificeres ved en kombination af ting. Tag leveringslinjen. Den har sit eget post-id, men hver enkelt er unik ved levering + produkt. Forestil dig, at vi gemmer leveringen, produktet, antallet og produktets beskrivelse.
Antallet giver mening her: det beskriver dette produkt på denne levering. Men produktbeskrivelsen beskriver ikke kombinationen; den beskriver produktet. Sæt den på hver leveringslinje, og vi gentager den, hver gang den køleskabsmodel kører ud på endnu en lastbil. Det er sporet, der viser, at kendsgerningen hører hjemme et andet sted.
Så 2NF-spørgsmålet er: beskriver denne attribut hele den ting, jeg identificerer, eller kun en del af den? Hvis den kun beskriver produktet, hører den til Produkt. Hvis den beskriver produktets plads på netop denne levering (antallet, om det skal installeres), hører den hjemme på leveringslinjen. Mange admins vil aldrig bevidst sige: »Nu anvender jeg anden normalform.« Det er fint. Jeg går meget mere op i, at du lærer at spørge: hvad beskriver denne kendsgerning faktisk?
Tredje normalform: gem ikke kendsgerninger om andre kendsgerninger
Sektion kaldt “Tredje normalform: gem ikke kendsgerninger om andre kendsgerninger ”Nu den, jeg går mest op i til hverdagens modellering: tredje normalform, eller 3NF.
Hvert distributionscenter har en leder på detailpartnerens side, en person, der bruger partnerportalen. Forestil dig, at vi sætter DC-lederens navn direkte på hver levering. Hvad sker der, når DC’et får en ny leder? Vi opdaterer hver levering fra det DC. Det er den samme dårlige lugt igen: vi gentager en kendsgerning, der hører hjemme et andet sted. Leveringen kører fra et DC; DC’et har en leder. Det er to relationer, og lederens navn hører til DC’et.
Den klassiske lærebogsudgave er adressen: postnummer, by og stat gemt sammen på hver post, når byen og staten som regel følger af postnummeret. Samme form, samme spørgsmål.
Det praktiske 3NF-spørgsmål er: beskriver denne kendsgerning denne ting, eller noget andet, denne ting hænger sammen med? Jeg vil have dig til at stille det hele tiden.
Træd nu et skridt tilbage, ligesom vi gjorde med hundene. Den leveringstabel, vi startede med, er blevet til fem ting, der hver indeholder sine egne kendsgerninger: leveringslinjen kom ud ved første normalform, produktet ved anden, og distributionscentrets by og leder flyttede over på distributionscentret ved tredje.
Samme tre spørgsmål, samme resultat: hver kendsgerning ét sted.
En enkel måde at tænke på 1NF, 2NF og 3NF
Sektion kaldt “En enkel måde at tænke på 1NF, 2NF og 3NF ”Du kan bruge et helt databasekursus på normalisering. Det gør vi ikke. I dette kursus vil jeg have, at du har en arbejdende arkitekts forståelse:
- 1NF: gemmer jeg flere værdier eller gentagne strukturer inde i et felt eller en post?
- 2NF: beskriver denne kendsgerning hele den ting, jeg identificerer?
- 3NF: beskriver denne kendsgerning virkelig denne entitet, eller noget, der hænger sammen med den?
Det er ikke definitioner til en lærebog om databaser. Det er spørgsmål, jeg rent faktisk kan bruge, mens jeg designer.
Ja, normalisering går længere: Boyce-Codd-normalform (BCNF) strammer reglerne omkring 3NF for modeller med mere komplicerede nøgler, og der findes fjerde, femte og endda sjette normalform. De handler om sjældnere former for gentagelse, den slags, der kommer af usædvanlige nøglestrukturer mere end af hverdagens forretningsposter. En masse teori, og efter min erfaring ikke hovedpinen værd for den slags systemer, vi bygger på Salesforce. I forretningssystemer går presset som regel den anden vej: det er langt mere sandsynligt, at vi træder et skridt tilbage fra tredje normalform, for brugernes, rapporteringens og ydeevnens skyld, end at vi går forbi den. Det er næste modul. Vid, at de videre former findes, men til det meste af det, vi laver, giver 1NF til 3NF os de tankeværktøjer, vi har brug for.
Design til tredje normalform
Sektion kaldt “Design til tredje normalform ”Her er min holdning. Når jeg prøver at forstå en forretning og designe dens logiske model, vil jeg starte rent: kendsgerninger om leveringen hos leveringen, kendsgerninger om distributionscentret hos DC’et, kendsgerninger om lastbilen hos lastbilen, gentagne begreber modelleret som begreber. Jeg vil ikke have en forretningskendsgerning kopieret ud over hele modellen, bare fordi det måske gør en skærm eller en rapport nemmere. Ikke endnu.
Men normalisering er ikke en religion. Målet er ikke den mest akademisk rene model, der overhovedet kan lade sig gøre. Jeg har set arkitekter blive så optaget af renhed, at modellen bliver en pine for alle, der skal bruge den. Vi prøver ikke at vinde en normaliseringskonkurrence; vi bygger et system, og det system har brugere, rapporter, integrationer, krav til ydeevne, skala og i stigende grad AI, der læser dataene. De vil måske have dataene i andre former.
Så mit udgangspunkt er design til tredje normalform, hvor det kan lade sig gøre, og træd så et skridt tilbage, når det er nødvendigt: forstå først, hvor hver kendsgerning hører hjemme. Hvis vi så vil bryde med det, vil jeg vide hvorfor. Hvis svaret er »fordi jeg ikke vidste, hvor jeg ellers skulle placere feltet«, er det ikke godt nok. Hvis svaret er »vi har bevidst kopieret denne værdi, fordi det gør partnerens scorecard enkelt, og vi har en pålidelig måde at holde den synkroniseret på«, så er vi i gang med en arkitektursamtale.
Hvad en arkitekt beslutter her
Sektion kaldt “Hvad en arkitekt beslutter her ”Når jeg normaliserer en logisk model, leder jeg efter:
- Gentagne værdier eller strukturer, der bør modelleres for sig.
- Attributter, der beskriver en anden entitet end den, de er gemt på.
- Kendsgerninger, der unødigt er kopieret ud på mange poster, og de afhængigheder, der får dem til at blive inkonsistente.
- Forretningsbegreber, der gemmer sig inde i felter.
- En ren model, der viser sandheden, før der indgås kompromiser i implementeringen.
Ikke fordi ethvert fysisk system skal forblive perfekt normaliseret. Tværtimod. I det næste modul bryder vi med vilje med den rene model, for brugernes, rapporteringens, skalaens, analysens og AI’s skyld, og vi gør det med fuld viden om, hvad vi bytter væk.
