Utover relasjonsmodellen
Når 1NF blir en tvangstrøye. Komplekse typer, JSON, MapReduce, NoSQL, CAP og datalager — og når de gir mening framfor (eller sammen med) en klassisk RDBMS.
Når relasjonsmodellen blir en tvangstrøye
Den relasjonelle modellen er strålende for det den er bygd for: strukturerte data med klare skjemaer, sterke integritetskrav og spørringer på tvers av relasjoner. Men over de siste tjue årene har tre press-punkter blitt umulige å overse — og de drev fram både SQL-utvidelser og helt nye familier av databasesystemer.
Svaret kom i to spor: utvid SQL (objekt-relasjonelle typer, JSON, arrays — alt fortsatt i én transaksjonell motor) og bygg nye systemer (NoSQL, distribuerte filsystemer, MapReduce). Et godt tommelfingerregel: relasjoner først, andre verktøy bare når relasjonsdata-modellen faktisk sliter.
"NoSQL" betydde opprinnelig "Not SQL", men brukes i dag oftere om "Not Only SQL". De fleste produksjonsstacker er hybride: en RDBMS for kjernen, og spesialdatabaser for spesifikke arbeidsmengder.
Klasser, arv og pekere — i SQL
Ren relasjonsmodell krever 1NF: hver attributtverdi er atomær. Det er en tvangstrøye for naturlig strukturerte data — adresser, koordinater, lister av interesser. Standard-SQL siden 1999 lar deg definere strukturerte typer, arve dem, og bruke arrays og pekere — uten å forlate relasjonsmotoren.
Strukturerte typer (UDT)
CREATE TYPE Address AS (
street VARCHAR(60),
city VARCHAR(40),
zip CHAR(4)
);
CREATE TYPE Person AS (
name VARCHAR(40),
address Address, -- nested struct
phones VARCHAR(20) ARRAY[3] -- array av primitiver
);
En strukturert type grupperer flere felt under ett attributt-navn. Du leser dem med dot-notasjon: p.address.city. Arrays og multisets gjør 1NF til en valgfri restriksjon, ikke en lov.
Type-arv
CREATE TYPE Student UNDER Person (
totCredits INTEGER,
degree VARCHAR(20)
);
SQL skiller mellom type-arv (typene arver felt og metoder) og tabell-arv (subtabeller arver rader fra supertabellen). I PostgreSQL er det syntaks: CREATE TABLE student () INHERITS (person);. Da ligger hver studentrad også i person-spørringer — om du ikke skriver SELECT * FROM ONLY person.
Pekere (referansetyper)
I stedet for å lagre fremmednøkkel-attributter og selv stå for join-en, lar referansetyper deg si "dette feltet peker på en rad i tabellen people":
CREATE TABLE articles (
title VARCHAR(120),
author REF(Person) SCOPE people
);
-- Path-uttrykk: følger pekeren uten eksplisitt JOIN
SELECT a.title, a.author->name
FROM articles AS a;
Strukturerte typer, arv og REF brukes relativt sjelden i moderne PostgreSQL/MySQL — de fleste velger heller JSONB-kolonner eller en rein normalisert relasjon. Men eksamen kan spørre om syntaks og semantikk.
SELECT * FROM person finner også studenter, om du ikke skriver FROM ONLY person.articles.author REF(Person) SCOPE people. Hva gjør a.author->name i et SELECT?x->y dereferanser pekeren og henter feltet y fra mål-raden. Det er sukker over en JOIN.Skjema som kan, men ikke må
Semi-strukturert data har noe struktur (taggene/nøklene er der), men forskjellige poster trenger ikke ha samme felt. To formater dominerer: JSON for moderne webtjenester, XML for eldre B2B-utveksling og dokumenter.
{ }) eller arrays ([ ]); løv er primitiver (tall, strenger, booleaner, null).JSON i moderne SQL
PostgreSQL har JSON (rå tekst) og JSONB (binær, indekserbar). MySQL har en analog JSON-type. Å lagre JSON i en kolonne er ikke "juks" — det gir deg fleksibelt skjema for de feltene som varierer, mens fremmednøkler og indekser fortsatt fungerer på de som er stabile.
SELECT
data->>'name' AS name,
data->'emner'->0
->>'kode' AS firstcourse,
jsonb_array_length(
data->'emner') AS n_courses
FROM students
WHERE data @> '{"campus":"NTNU"}';
-> | Hent felt som JSON |
->> | Hent felt som tekst |
#> / #>> | Path som array |
@> | Inneholder (containment) |
? | Har nøkkel |
SQL/JSON-standarden gir også JSON_VALUE, JSON_QUERY og JSON_TABLE (det siste flater ut JSON til vanlige rader). Du kan opprette en GIN-indeks på en JSONB-kolonne for raske containment-spørringer.
XML — eldre, men ikke borte
XML er fortsatt vanlig i bank, helse og myndighet. Spørrespråket er XPath/XQuery:
/* XPath-uttrykk i XQuery */
for $b in doc("books.xml")//book
where $b/price < 100
return $b/title
Konfig-pakker, audit-payloads, ymse "metadata"-bag-attributter, fleksible produktattributter (skjema varierer per kategori). Når alle felt er stabile og brukes mye i WHERE/JOIN — normaliser dem heller.
data->'emner' versus data->>'emner' i PostgreSQL?-> når du fortsetter å navigere i JSON-strukturen, ->> bare når du tar ut en endelig tekst-/skalarverdi.Klasser i koden, tabeller på disk
Selv med UDT i SQL ligger applikasjonens klasser i et programmeringsspråk og dataene i en relasjonsdatabase — to verdener. ORM-rammeverk (Hibernate, Django ORM, SQLAlchemy, JPA) kobler dem: hver klasse mappes til en tabell, hver instans til en rad, og navigasjon mellom objekter (person.address.city) blir til implisitte joins.
ORM gir deg
- Skjema-migreringer som kode
- Portabilitet på tvers av RDBMS
- Mindre boilerplate for CRUD
- Naturlig domenemodell i applikasjonskoden
ORM koster
- "N+1-problemet" når lazy loading skjuler joins
- Bulk-oppdateringer er klønete
- Komplekse spørringer ender ofte som rå-SQL likevel
- Skjuler det som faktisk koster (indekser, planer)
Object-relasjonelle SQL-features og ORM-rammeverk løser samme grunnleggende friksjon — bare på hver sin side. Begge er nyttige, men sjelden noe svar i seg selv på "skal jeg bruke noe annet enn relasjonsmodellen?".
Når data sprenger én maskin
"Big data" er et utflytende begrep. Den klassiske definisjonen er Doug Laneys 3 V-er:
Distribuerte filsystemer — HDFS
Når én disk er for liten, deler vi data over mange maskiner. HDFS (Hadoop Distributed File System), inspirert av Googles GFS, er det kanoniske eksempelet:
- NameNode — én master holder metadata: hvilke filer finnes, hvilke blokker hører til, hvor ligger blokkene.
- DataNodes — mange maskiner som lagrer faktiske blokker (typisk 64–128 MB hver).
- Replikering — hver blokk lagres i flere kopier (default 3) på forskjellige noder, så feil på én maskin ikke mister data.
- Optimalisert for store filer (titalls MB til hundrevis av GB), append-mønster, og scan-tunge spørringer — ikke for mange små filer eller in-place updates.
HDFS er bare et filsystem. Det vet ingenting om skjema, rader eller transaksjoner. Det er bærebjelken man bygger Hive, Spark, HBase og MapReduce-jobber oppå.
Programmeringsmodellen som starter alt
MapReduce er et programmeringsmønster der du skriver to rene funksjoner, og rammeverket tar seg av all koordineringen — parallellisering, sortering, feiltoleranse, gjenstart av krasjede maskiner.
map(record) → emit(key, value) kjøres på hver inn-record uavhengig, parallelt, der dataene ligger.
reduce(key, list<value>) → output kjøres på alle verdiene som delte samme key etter en automatisk shuffle.
Alle relasjonelle operasjoner kan uttrykkes med map+reduce — men i praksis bruker man heller Apache Spark (DAG av operatorer, lazy evaluation, in-memory caching) eller SQL-laget Hive/Pig som kompilerer til MapReduce eller Spark.
Fire typer dataforhold, fire datamodeller
NoSQL-bevegelsen er ikke ett system, men en familie av spesialiserte datamodeller. Hver er optimalisert for et bestemt aksess-mønster og ofrer noe (oftest sterk konsistens og rik spørring) for å vinne noe annet (skala, lav latens, fleksibelt skjema).
Cache, sesjons-data, leaderboards. Ekstremt rask når du vet nøkkelen. Ingen joins eller spørring på verdier.
CMS, produktkataloger, brukerprofiler. Dokumenter med varierende felt; rik filtrering og indeksering inne i JSON.
Tidsserier, IoT, analytics-tabeller med milliarder rader. Sparse skjema, rask write, range-skann på radnøkkel.
Sosiale nettverk, anbefalinger, fraud-deteksjon. Spørringer langs stier blir billige der rekursive joins er smertefulle.
Eksempel: samme data, fire modeller
Si vi vil lagre at Alvalds (id 12345) tar emnet TDT4145:
db.students.insertOne({
_id: 12345,
name: "Alvalds",
emner: [
{ kode: "TDT4145", sem: "V26" },
{ kode: "TDT4173", sem: "V26" }
]
})
db.students.find({
"emner.kode": "TDT4145"
})
CREATE (s:Student {id:12345, name:"Alvalds"}),
(c:Course {kode:"TDT4145"}),
(s)-[:TAR {sem:"V26"}]->(c);
// Hvem tar TDT4145?
MATCH (s:Student)-[:TAR]->(:Course {kode:"TDT4145"})
RETURN s.name;
MATCH-pattern langs to :VENN-kanter.Hvorfor du må velge — interaktivt
Eric Brewers CAP-teorem: et distribuert system kan garantere maksimalt to av tre egenskaper når en nettverkspartisjon oppstår.
- C — Consistency: alle noder ser samme data samtidig. Etter en skriv leser alle den nye verdien.
- A — Availability: hver request får et svar (riktig eller stale, men ikke en feil).
- P — Partition tolerance: systemet fortsetter å fungere selv om nettverket deler seg i isolerte øyer.
Siden nettverkspartisjoner faktisk skjer i praksis, er P obligatorisk i ekte distribuerte systemer. Det reelle valget er C eller A — under partisjon:
BASE — alternativet til ACID
NoSQL-systemer som velger AP (tilgjengelighet over konsistens) trenger en svakere kontrakt enn ACID. Den heter BASE:
"Eventuell konsistens" betyr ikke "ingen konsistens". Det betyr at vinduet hvor en leser kan se utdaterte data er begrenset, ikke null. For en sosial feed er det helt greit; for et bankoppgjør er det ikke det.
To verdener: online og analytisk
Den samme bedriftens database brukes til to vidt forskjellige typer arbeid. OLTP (online transaction processing) og OLAP (online analytical processing) har så ulike krav at det vanligvis lønner seg å skille dem fysisk.
| OLTP | OLAP | |
|---|---|---|
| Spørringer | Mange, korte, nøkkel-baserte | Få, lange, store skann + aggregeringer |
| Skriv | Hyppig, kritisk | Periodisk batch-load |
| Skjema | Normalisert (3NF/BCNF) | Denormalisert (star/snowflake) |
| Lagring | Rad-orientert | Kolonne-orientert (komprimerer bedre) |
| Eksempler | PostgreSQL, MySQL, Oracle OLTP | Snowflake, BigQuery, Redshift, Vertica |
Star-schema
Sentralt i et datalager står fakta-tabellen: en bred tabell med målbare hendelser (salg, klikk, transaksjon) og fremmednøkler til dimensjons-tabeller som beskriver konteksten.
OLAP-operasjoner
- Slice / dice — fikser én eller flere dimensjoner (f.eks. "vis bare salg i 2025 og kategori = elektronikk").
- Drill-down / roll-up — finere/grovere granularitet (måned → uke → dag, eller motsatt).
- Pivot — bytt akser i pivot-tabellen.
- Data cube — pre-aggregerte sum-tabeller på alle subset av dimensjoner. SQL:
GROUP BY CUBE(date, store, item).
ETL og datalagre
Data flyter fra OLTP-systemer til datalageret via ETL (Extract → Transform → Load): rensing, deduplisering, formatkonvertering. Moderne varianter snur til ELT (load først, transformer i lageret) for å utnytte parallelle motorer.
Data lake er et alternativ: rå data i alle formater i et billig filsystem (typisk S3 / HDFS), uten skjema-pålegging på inntak. Skjema-on-read gir fleksibilitet, men flytter kompleksiteten til hver spørring.
SELECT SUM(qty) FROM sales WHERE year=2025 trenger to kolonner av kanskje tjue. Rad-orientert layout tvinger oss til å lese alle. Kolonne-orientert + run-length encoding gir 10×–100× speedup.Et beslutnings-cheatsheet
Det vanlige svaret er kjedelig, men sant: start med en relasjonsdatabase. Bytt eller utvid kun når et konkret krav tydelig ikke passer modellen.
| Behov | Velg | Hvorfor |
|---|---|---|
| Strukturert kjernedata, transaksjoner, integritet | RDBMS (Postgres, MySQL) | ACID, joins, modne verktøy. Fortsatt det riktige standardvalget. |
| Fleksibelt skjema med noen variable felt | RDBMS + JSONB | Det beste fra begge — relasjon for det stabile, JSON for det varierende. |
| Cache, sesjoner, leaderboards | Key-value (Redis) | Ekstremt rask get / put, ingen behov for joins. |
| Dokumenter med varierende felt, høyt volum | Dokument-DB (MongoDB) | JSON-spørring, indekser inne i dokumenter, horisontal skala. |
| Tidsserier, IoT-events, milliarder rader | Wide-column (Cassandra) / tidsserie-DB | Skriv-tungt, range-skann etter (entity, time), partisjons-vennlig. |
| Stier, sosiale grafer, anbefaling | Grafdatabase (Neo4j) | Sti-spørringer på flere hopp blir billige. |
| Analytics, dashboards, BI | Datalager (Snowflake, BigQuery) | Kolonne-lagring, MPP-spørringsmotor, optimalisert for store skann. |
| Råe events for senere analyse | Data lake (S3 + Spark) | Ingen forhåndsforpliktelse til skjema, billig lagring. |
Når ikke droppe relasjonsmodellen
- Du tror du trenger skala — men er ikke i nærheten av terabyte-grensen ennå.
- "Skjema-fritt" høres frigjørende ut — helt til hvert Java-deserialisering må håndtere fem ulike historiske formater.
- Joins er upraktiske — eller bare manuelle, fordi NoSQL-en ikke har dem.
- "Vi blir konsistent etter hvert" passer ikke for penger, lager-trekk, juridiske bestillinger.
Forvent spørsmål som ber deg begrunne et valg mellom relasjons-DB, dokument-DB, graf-DB, datalager. Gode svar nevner: type spørringer (oppslag vs sti vs aggregering), skala, skjema-stabilitet, og konsistenskrav (ACID vs eventually consistent).
Oppsummering
Vil du teste deg selv på hele kapittelet samlet, finnes det en avsluttende quiz på kapittel-oversikten.