Datamodellen tot SQL-standaard
Waarom “databasegeschiedenis” ineens heel praktisch wordt
Stel je bouwt als data engineer een platform waar meerdere teams tegelijk klantgegevens bijwerken: support past adressen aan, finance registreert betalingen, en marketing segmenten klanten. Alles moet consistent blijven, ook als er gelijktijdige updates plaatsvinden of als een systeem opnieuw opstart na een storing. Op dat moment merk je dat een database niet “gewoon opslag” is, maar een afspraak over structuur, regels en gedrag.
In deze les kijk je naar hoe die afspraken zijn ontstaan: van vroege datamodellen tot het moment waarop SQL uitgroeit tot een breed geaccepteerde standaard. Dat geeft je een nuttig kompas: waarom relationele tabellen zo dominant werden, welke problemen ze oplossen, en waarom sommige “oude” keuzes (zoals schema’s en constraints) nog steeds de basis zijn onder moderne data-platforms.
De kernbegrippen: datamodel, schema, en SQL als contract
Een datamodel is een manier om data te beschrijven: welke objecten bestaan er (bijv. Klant, Order), hoe ze zich tot elkaar verhouden, en welke regels gelden. Het model is het denkraam; een schema is de concrete implementatie daarvan in een database (tabellen/kolommen, datatypes, constraints). Een DBMS (Database Management System) is de software die opslag, bevraging, transacties en beveiliging regelt.
SQL kun je zien als een contracttaal tussen mens/applicatie en database: je beschrijft wat je wil (bijv. “alle orders van klant X”), en de database bepaalt hoe dat efficiënt uitgevoerd wordt. Een belangrijk onderscheid is tussen:
-
DDL (Data Definition Language): structuur definiëren (CREATE/ALTER/DROP).
-
DML (Data Manipulation Language): data lezen/wijzigen (SELECT/INSERT/UPDATE/DELETE).
-
Constraints: regels afdwingen (PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, NOT NULL).
Een nuttige analogie: een datamodel is een bouwtekening, het schema is het gebouw, en SQL is het formulier waarmee je mensen binnenlaat, kamers reserveert of inventaris verplaatst—met de huisregels ingebouwd.
Van bestandsopslag naar datamodellen: waarom “losse files” niet schaalden
In de vroege dagen werden bedrijfsgegevens vaak opgeslagen als platte bestanden (bijv. per applicatie een eigen bestand/structuur). Dat is simpel om te starten, maar veroorzaakt snel structurele problemen zodra data gedeeld wordt. Het bekendste effect is redundantie: dezelfde klantnaam staat in meerdere files, met kans op verschillen. Als support een adres wijzigt maar finance niet, krijg je inconsistentie die je later met dure opschoning moet herstellen.
Daarbovenop komt het probleem van data-afhankelijkheid: de applicatie “kent” de fysieke structuur (positie in record, fixed-width velden). Een kleine wijziging in het bestand kan meerdere applicaties breken. Dit staat haaks op het idee van data-onafhankelijkheid: dat applicaties op een stabiele, logische manier met data werken terwijl de fysieke opslag kan evolueren.
Best practices die hieruit groeiden:
-
Centraliseer waarheid: één bron voor kernentiteiten (single source of truth) in plaats van duplicatie per applicatie.
-
Scheiding logisch vs fysiek: ontwerp eerst het model en laat een DBMS opslag/indices optimaliseren.
-
Maak regels expliciet: constraints in de database in plaats van “afgesproken in code”.
Veelvoorkomende valkuilen en misvattingen:
-
Misvatting: “We lossen inconsistentie op met een nightly batch.” In werkelijkheid groeit inconsistente data vaak sneller dan je batch kan repareren, en je verliest realtime betrouwbaarheid.
-
Valkuil: businessregels alleen in applicatiecode zetten. Dan krijg je verschillende interpretaties per service of team; de database kan het niet afdwingen.
-
Misvatting: “Opslag is het probleem.” Meestal is coördinatie het probleem: gelijktijdige updates, integriteit en gedeeld begrip van definities.
Hiërarchisch en netwerkmodel: snel voor navigatie, lastig voor verandering
Voor relationeel dominant werd, waren hiërarchische en netwerkdatabases invloedrijk. In een hiërarchisch model organiseer je data als een boom: één parent met meerdere children. Dat werkt goed voor natuurlijke hiërarchieën (bijv. organisatie → afdelingen → medewerkers) en kan zeer efficiënt zijn als je altijd dezelfde paden bewandelt. Het nadeel: de structuur is rigide; veel-op-veel relaties zijn onnatuurlijk, en een extra toegangspad vereist vaak structurele wijzigingen.
Het netwerkmodel (zoals CODASYL) generaliseert dit met sets/links zodat records meerdere relaties kunnen hebben. Daarmee los je een deel van de rigiditeit op, maar je ruilt het in voor complexiteit: queries worden “navigational”. Je beschrijft niet “geef me alle orders van klant X”, maar eerder: start bij klant, volg pointer naar order-set, itereren, enzovoort. Applicaties worden daardoor sterk gekoppeld aan de paden in de database.
Waarom dit belangrijk is voor jou als data engineer: deze modellen laten een terugkerend trade-off zien tussen performance via vaste paden en flexibiliteit via declaratieve queries. In moderne systemen zie je dezelfde spanning terug: bijvoorbeeld in datastores die extreem snel zijn voor één access pattern, maar pijnlijk worden zodra je vragen veranderen.
Typische pitfalls/misconcepties:
-
Misvatting: “Snelheid betekent betere architectuur.” Als een model snel is voor één pad maar elke nieuwe vraag een redesign vraagt, betaal je later met interest.
-
Valkuil: data modelleren rond schermen/rapporten (“we moeten deze lijst tonen”) in plaats van rond stabiele entiteiten en relaties.
-
Best practice: ontwerp voor veranderende vragen—zeker in analytische en platformcontext, waar nieuwe teams nieuwe queries brengen.
Het relationele model: data als sets, integriteit als eerste klas burger
Het relationele model (Codd) veranderde het spel door data te beschrijven als relaties (tabellen): verzamelingen van tuples (rijen) met attributen (kolommen). De echte innovatie is niet “tabellen”; het is het idee dat je met set-based, declaratieve operaties werkt. Je zegt wat je wilt, en de database kan optimaliseren met indexes, join-orders, en execution plans. Dit maakt vragen flexibeler: dezelfde data kan meerdere querypatronen ondersteunen zonder dat je fysieke pointers hoeft te volgen.
Een tweede pijler is normalisatie: een methode om redundantie en update-anomalieën te verminderen door data op te splitsen in logisch consistente tabellen. Denk aan de klassieke problemen:
-
Insert anomaly: je kunt een klant niet registreren zonder meteen een order te verzinnen.
-
Update anomaly: je moet een adres op vijf plekken wijzigen.
-
Delete anomaly: je verliest klantinfo als de laatste order verdwijnt.
Normalisatie (bijv. tot 3NF) probeert dit te voorkomen door feiten één keer vast te leggen en relaties via sleutels te modelleren. Daar hoort referentiële integriteit bij: foreign keys garanderen dat een order niet verwijst naar een niet-bestaande klant. Dit is precies het soort betrouwbaarheid dat je nodig hebt wanneer meerdere teams gelijktijdig schrijven.
Veelvoorkomende misvattingen:
-
Misvatting: “Normalisatie is altijd beter.” Te ver normaliseren kan queries duur maken (veel joins) of ontwikkelteams frustreren; je zoekt een balans tussen integriteit, performance en begrijpelijkheid.
-
Misvatting: “Constraints vertragen alles.” Constraints kosten iets, maar ze voorkomen vaak veel grotere kosten door datakwaliteitsproblemen en herstelacties.
-
Best practice: modelleer sleutels bewust (surrogate vs natural) en leg integriteitsregels vast in het schema, niet alleen in ETL-code.
SQL wordt standaard: één taal, veel implementaties
SQL groeide uit tot de dominante interface voor relationele databases omdat het twee werelden koppelt: wiskundige basis (relationele algebra/calculus) en praktische toepasbaarheid (DDL, DML, views, privileges). Maar “SQL” is niet één monolithisch iets. Er is een standaard (ANSI/ISO) en er zijn dialecten (verschillen per DBMS). In data engineering merk je dit zodra je van de ene database naar de andere migreert, of wanneer je pipelines bouwt die op meerdere engines moeten draaien.
Een nuttige manier om SQL te begrijpen is als drie lagen:
- Model en integriteit (DDL + constraints): definieert wat “geldige data” is.
- Toegang en transactiegedrag (DML + transacties): bepaalt hoe lezen/schrijven gebeurt in de tijd.
- Portabiliteit en tooling (standaard vs dialect): bepaalt hoe herbruikbaar je queries en schema’s zijn.
SQL’s succes komt ook doordat het goed samengaat met query optimalisatie: de engine kan dezelfde declaratieve query op meerdere manieren uitvoeren. Daardoor kun je indexen toevoegen, tabellen partitioneren, of opslag aanpassen zonder je applicatiequery’s volledig te herschrijven—ten minste in theorie.
Typische valkuilen in de praktijk:
-
Dialect-lock-in: je gebruikt vendor-specifieke functies (bijv. specifieke merge-syntax of types) waardoor migratie duur wordt.
-
Semantische verschillen: NULL-handling, string-collations, timestamp/timezone gedrag lijken klein, maar veroorzaken subtiele bugs in data producten.
-
Best practice: wees expliciet over datatypes, constraints en tijdzones, en documenteer waar je bewust afwijkt van “portable SQL”.
Datamodellen naast elkaar: wanneer welk denkkader wringt
| Dimensie | Bestands-/file-based | Hiërarchisch / netwerk | Relationeel + SQL |
|---|---|---|---|
| Kernidee | Data per applicatie in eigen bestanden; structuur vaak impliciet in code. | Data als records met vooraf gedefinieerde paden (boom of graf) die je navigeert. | Data als tabellen (sets) met declaratieve bevraging en expliciete integriteitsregels. |
| Sterkte | Eenvoudig te starten; laagste drempel. | Zeer snel voor vaste, bekende toegangspaden. | Flexibel voor nieuwe vragen; sterke integriteit en onafhankelijkheid tussen logisch en fysiek. |
| Zwaktes | Redundantie, inconsistente definities, breekbaar bij schemawijzigingen. | Star bij veranderende requirements; query’s sterk gekoppeld aan structuur/paden. | Kan complex worden (joins, schema-ontwerp); performance vraagt ontwerpdiscipline (indexen/keys). |
| Typische valkuil | “We fixen het later met batch jobs.” | Modelleren rond één navigatiepad en later vastlopen bij nieuwe query’s. | Denken dat SQL automatisch “portable” is en constraints overslaan om sneller te shippen. |
| Wanneer je het vandaag nog voelt | CSV’s/exports zonder contract; shadow data in teams. | Sleutel-waarde stores of graph-achtige use-cases waar navigatie dominant is. | OLTP-systemen, data platforms, analytics engines met SQL-interfaces. |
[[flowchart-placeholder]]
Twee voorbeelden zoals je ze in data engineering echt tegenkomt
Voorbeeld 1: klant- en orderdata van “Excel + exports” naar relationeel schema
Een organisatie start met een gedeelde spreadsheet: tabbladen “Customers” en “Orders”, plus exports uit een webshop. Na een paar maanden blijkt dat dezelfde klant meerdere keren voorkomt (“Jansen”, “Janssen”), en dat orders soms verwijzen naar een klant-id die niet bestaat omdat iemand kolommen heeft verschoven of een export handmatig heeft bewerkt. Rapportages (“omzet per klant”) worden discussies over welke versie klopt.
De relationele aanpak begint met het modelleren van stabiele entiteiten en relaties. Je maakt een Customer-tabel met een primaire sleutel en unieke regels (bijv. unieke email als de business dat toelaat), en een Order-tabel met een foreign key naar Customer. Daardoor kan een order niet “los” bestaan, en dwing je af dat basale definities kloppen. Je verplaatst validatie van “tribal knowledge” naar het schema, zodat elk team dezelfde regels gebruikt.
De impact is praktisch: minder silent failures, minder correctie-work, en voorspelbare joins voor downstream. De beperking is dat je hier nog niet alles oplost: deduplicatie (record linkage), historisering (adreswijzigingen door de tijd), en late-arriving data blijven ontwerpkeuzes. Maar je hebt nu een contract dat je pipelines en applicaties kunnen vertrouwen, in plaats van een fragiele set bestanden.
Voorbeeld 2: productcatalogus met categorieën — waarom hiërarchisch intuïtief is, maar relationeel schaalbaar
Neem een productcatalogus met categorieën (“Elektronica → Audio → Koptelefoons”). Een hiërarchisch model voelt logisch: elke categorie heeft kinderen, en producten hangen onder een category node. Zolang je één pad gebruikt (“toon alles onder Elektronica”), is dit efficiënt. Maar zodra marketing vraagt om een product in meerdere categorieën (bijv. “Sport” én “Wearables”) of zodra je cross-cuts wilt (“alle producten met noise cancelling ongeacht categorie”), begint een puur hiërarchische structuur te wringen.
Relationeel modelleer je dit als tabellen Category, Product, en een koppel-/associatietabel ProductCategory (veel-op-veel). Daarna kun je zowel hiërarchisch browsen (met parent_id of closure table-varianten) als flexibel taggen en filteren. SQL maakt het mogelijk om nieuwe vragen te stellen zonder dat je de opslagstructuur volledig hertekent—je schrijft een andere query in plaats van een nieuw pointerpad in te bouwen.
De winst zit in aanpasbaarheid: nieuwe classificaties, tijdelijke campagnes, en multi-toewijzingen worden datavariaties, geen databaserebuilds. De beperking is dat hiërarchische queries (bijv. “alle descendants”) extra denkwerk vragen in SQL, en performance vaak index- en modelkeuzes vereist. Toch is dit precies waarom SQL en het relationele model zo lang dominant blijven: ze geven je ruimte om vragen te laten evolueren zonder je fundament elke keer te slopen.
De rode draad: waarom SQL-standaardisatie ertoe doet
De geschiedenis van datamodellen is in de kern een geschiedenis van trade-offs: snelheid versus flexibiliteit, lokale optimalisatie versus gedeelde waarheid, impliciete afspraken versus expliciete constraints. SQL won omdat het relationele model een stabiele basis gaf om die trade-offs beheersbaar te maken, en omdat standaardisatie (met alle imperfecties) een gezamenlijke taal gaf aan tools, teams en platforms.
Belangrijk om vast te houden:
-
Datamodellen bepalen gedrag, niet alleen opslag.
-
Integriteit hoort in het schema, niet als “best effort” in losse scripts.
-
SQL is declaratief én een contract: krachtig, maar je moet bewust omgaan met dialecten en semantiek.
Next, we’ll build on this by exploring OLTP, OLAP & datawarehousing [20 minutes].