Leer Databases & SQL vanaf nul
1Wat een relationele database is
Een relationele database slaat gegevens op in tabellen. Een tabel lijkt op een spreadsheet: elke rij is één record, elke kolom is één eigenschap.
Een winkel kan een tabel customers hebben met de kolommen id, name en city, en een tabel orders met de kolommen id, customer_id en total.
Een primaire sleutel is een kolom, of een set kolommen, die elke rij uniek identificeert. Ze kan nooit worden herhaald en nooit leeg zijn. Hier is dat de id van elke tabel.
Een vreemde sleutel (foreign key) is een kolom die verwijst naar de primaire sleutel van een andere tabel. In orders zegt customer_id welke klant de bestelling plaatste.
Zo worden tabellen gekoppeld: één klant kan veel bestellingen hebben, maar elke bestelling hoort bij één klant. Dat is een een-op-veel-relatie.
SQL (Structured Query Language) is de taal waarmee je tabellen maakt, gegevens wijzigt en er vragen over stelt.
Zelftoets Wat is een primaire sleutel?
- De eerste kolom van elke tabel
- Een index die zoekopdrachten sneller maakt
- Een kolom die naar een rij in een andere tabel verwijst
- Een kolom of set kolommen die elke rij uniek identificeert
Antwoord: Een kolom of set kolommen die elke rij uniek identificeert. Een primaire sleutel is uniek en nooit NULL, dus ze wijst precies één rij aan. Een kolom die naar een andere tabel verwijst, is een vreemde sleutel.
2Gegevens lezen met SELECT
De meeste vragen gaan over de SELECT-opdracht. Ze is opgebouwd uit clausules, altijd in deze volgorde geschreven:
SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY …
- SELECT noemt de kolommen die je wilt. DISTINCT verwijdert dubbele rijen.
- FROM noemt de tabel, of de tabellen die aan elkaar gekoppeld zijn.
- WHERE houdt alleen de rijen over die aan een voorwaarde voldoen, bijvoorbeeld WHERE total > 20.
- GROUP BY stopt rijen met dezelfde waarde in één groep, zodat je per groep kunt tellen of optellen.
- HAVING houdt alleen de groepen over die aan een voorwaarde voldoen. Ze mag aggregaten gebruiken, wat WHERE niet mag.
- ORDER BY sorteert het resultaat, standaard oplopend; voeg DESC toe voor hoogste eerst.
De aggregatiefuncties vatten veel rijen samen in één waarde: COUNT, SUM, AVG, MIN en MAX.
De database voert de clausules niet uit in de volgorde waarin je ze schrijft. Ze voert eerst FROM uit, dan WHERE, GROUP BY, HAVING, SELECT en ten slotte ORDER BY.
Andere opdrachten wijzigen gegevens: INSERT voegt rijen toe, UPDATE wijzigt rijen en DELETE verwijdert rijen. CREATE, ALTER en DROP wijzigen de tabellen zelf.
Zelftoets Je wilt alleen de klanten met meer dan 5 bestellingen. Welke clausule filtert op COUNT(*) > 5?
- DISTINCT
- HAVING
- ORDER BY
- WHERE
Antwoord: HAVING. De telling bestaat pas nadat de rijen zijn gegroepeerd, en HAVING filtert groepen. WHERE wordt vóór het groeperen uitgevoerd, dus kan geen aggregaat zoals COUNT gebruiken.
3Joins, normalisatie en transacties
Een join combineert rijen van twee tabellen met een voorwaarde, meestal vreemde sleutel = primaire sleutel:
FROM customers c JOIN orders o ON o.customer_id = c.id
- INNER JOIN (of JOIN) houdt alleen rijen over die in beide tabellen een match hebben.
- LEFT JOIN houdt elke rij van de linkertabel. Waar geen match is, zijn de kolommen van de rechtertabel NULL.
- NULL betekent dat een waarde ontbreekt of onbekend is. Toets het met IS NULL, nooit met = NULL.
Normalisatie splitst tabellen zodat elk feit één keer wordt opgeslagen. Zo voorkom je dat hetzelfde feit op meerdere plaatsen moet worden bijgewerkt.
- 1NF: elke cel bevat één atomaire waarde, zonder herhalende groepen.
- 2NF: 1NF, en elke niet-sleutelkolom hangt af van de hele sleutel, niet van een deel van een samengestelde sleutel.
- 3NF: 2NF, en geen enkele niet-sleutelkolom hangt af van een andere niet-sleutelkolom.
Een transactie groepeert opdrachten tot één eenheid. COMMIT maakt haar definitief en ROLLBACK maakt haar ongedaan. Transacties volgen ACID: atomiciteit (alles of niets), consistentie, isolatie en duurzaamheid.
Zelftoets Een klant heeft geen bestellingen geplaatst. Wat gebeurt er met die klant in customers LEFT JOIN orders?
- De klant verschijnt één keer voor elke bestelling in de tabel
- De klant verschijnt één keer, met NULL in de bestelkolommen
- De klant wordt uit het resultaat weggelaten
- De query mislukt met een foutmelding
Antwoord: De klant verschijnt één keer, met NULL in de bestelkolommen. Een LEFT JOIN houdt elke rij van de linkertabel en vult de ontbrekende rechterkant met NULL. De klant weglaten is wat een INNER JOIN doet.
4De methode stap voor stap
Om een query te schrijven, of uit te zoeken wat een gegeven query teruggeeft:
- Zoek de tabellen die de gegevens bevatten, en hoe ze door sleutels zijn gekoppeld.
- Schrijf FROM met de joins. Kies LEFT JOIN als rijen zonder match moeten blijven.
- Voeg WHERE toe voor voorwaarden op afzonderlijke rijen.
- Als de vraag “per” of “voor elke” zegt, voeg dan GROUP BY en het aggregaat toe.
- Voeg HAVING toe voor voorwaarden op het aggregaat, en dan ORDER BY als de volgorde ertoe doet.
- Controleer het resultaat op een klein voorbeeld: som de rijen op na elke clausule, in de volgorde waarin de database ze uitvoert.
Benoem bij een theorievraag eerst het begrip (een sleutel, een jointype, een normaalvorm, een ACID-eigenschap) en kies dan de optie die bij de definitie past.
5Een uitgewerkt voorbeeld
Volg elke stap. Probeer de volgende stap te voorspellen voordat je hem opent.
Uitgewerkt voorbeeldDe tabel customers heeft de rijen (1, Ana), (2, Ben) en (3, Cas). De tabel orders heeft de rijen (id, customer_id) = (10, 1), (11, 1), (12, 2) en (13, 1). Wat geeft deze query terug? SELECT c.name, COUNT(o.id) FROM customers c LEFT JOIN orders o ON o.customer_id = c.id GROUP BY c.name
1Elke klant wordt gekoppeld aan de bestellingen met zijn id. Ana past bij bestellingen 10, 11 en 13. Ben past bij bestelling 12.
Cas heeft geen bestellingen. Omdat het een LEFT JOIN is, blijft Cas in het resultaat, met NULL voor o.id.
2- Ana, 10
- Ana, 11
- Ana, 13
- Ben, 12
- Cas, NULL
Dat zijn 5 rijen.
3GROUP BY c.name maakt drie groepen: Ana met 3 rijen, Ben met 1 rij en Cas met 1 rij.
4COUNT(o.id) telt alleen de waarden die niet NULL zijn. Ana krijgt 3 en Ben krijgt 1.
Cas heeft één rij, maar zijn o.id is NULL, dus Cas krijgt 0.
5Ana, Ben en Cas hebben samen 3 + 1 + 0 = 4 bestellingen, evenveel als het aantal rijen in orders. Met COUNT(*) zou Cas ten onrechte 1 krijgen, omdat COUNT(*) de rij zelf telt.
Antwoord Ana 3, Ben 1, Cas 0.
6Veelgemaakte fouten
- Een aggregaat gebruiken in WHERE. Voorwaarden op COUNT, SUM of AVG horen in HAVING.
- = NULL schrijven. Elke vergelijking met NULL is onbekend; gebruik IS NULL of IS NOT NULL.
- Een INNER JOIN gebruiken als rijen zonder match moeten blijven. Gebruik een LEFT JOIN.
- De joinvoorwaarde vergeten. Zonder ON wordt elke rij met elke rij gekoppeld (een cross join).
- Een kolom selecteren die niet in GROUP BY staat en ook niet in een aggregaat.
- DELETE (verwijdert rijen) en DROP TABLE (verwijdert de hele tabel) door elkaar halen.
- UPDATE of DELETE uitvoeren zonder WHERE. Het verandert elke rij in de tabel.
Zelftoets Een tabel heeft 10 rijen en de kolom col is in elke rij NULL. Wat geeft SELECT COUNT(col) terug?
- 0
- 10
- 1
- NULL
Antwoord: 0. COUNT(col) telt alleen waarden die niet NULL zijn, dus het geeft 0. COUNT(*) telt rijen en zou 10 teruggeven.
7Hoe tentamenvragen eruitzien
- Makkelijk: kernbegrippen en feiten. Wat SELECT, WHERE, ORDER BY of DELETE doen, primaire en vreemde sleutels, wat NULL betekent.
- Gemiddeld: concepten toepassen. De juiste query voor een taak, WHERE tegenover HAVING, inner tegenover outer joins, de normaalvormen, ACID.
- Moeilijk: gevallen en analyse. Subqueries, isolatieniveaus en hun anomalieën, BCNF, vensterfuncties, indexen en queryprestaties, SQL-injectie.
Traceer een gegeven query op een paar rijen in de volgorde FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY. De meeste valkuilen komen binnen drie rijen naar boven.
Leer het stap voor stap in de appLeer het stap voor stap in de app