# Analyse en ERD

Van een lap tekst van de klant naar een database die klopt: entities, attributes en relationships tekenen in een ERD, en wat normaliseren is.

Multimedia & Creatieve Technologie · 2.1 Programming principles · Les 6 · ERD · versie 1.0.0 · 2026-10-25

Tags: database, erd, sql

Slides: <https://slides.mctantwerp.be/nl/programming-principles/07-erd>

Speaker notes: <https://slides.mctantwerp.be/nl/programming-principles/07-erd.notes.md>

<!-- slide 2 -->

## Agenda

1. Eerst denken
2. Wat een ERD is
3. Entities
4. Attributes
5. Relationships
6. Normaliseren
7. Van ERD naar SQL
8. Opdrachten

<!-- slide 3 -->

## 1. Eerst denken

<!-- slide 4 -->

**Think before you begin.**

Een kolom hernoemen op papier kost je een gom. In een database waar al code en data aan hangt, kost het je een namiddag.

<!-- slide 5 -->

![Een Delfts blauw tegeltje met de spreuk: Bezint eer ge begint](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/bezint-eer-ge-begint.webp)

![Een affiche van Loesje: Ik denk altijd eerst goed na voordat ik iets stoms zeg](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/loesje.webp)

<!-- slide 6 -->

### Truth by comics

![De cartoon van de boomschommel in twaalf vakjes: hoe de klant het uitlegde, hoe de projectleider het begreep, hoe de analist het ontwierp, hoe de programmeur het schreef, tot wat de klant echt nodig had, een autoband aan een touw](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/tree-swing.webp)

<!-- slide 7 -->

![Dilbert-strip: de klant wil pas zeggen wat de software moet doen als Dilbert ze ontworpen heeft, en vraagt dan of hij ze kan ontwerpen zodat ze de requirements vertelt](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/dilbert-requirements.webp)

<!-- slide 8 -->

![Dilbert-strip: de baas vertelt nadat alles geprogrammeerd is dat de specificaties veranderd zijn, en Dilbert slaat met zijn hoofd op het bureau](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/dilbert-specs-changed.webp)

<!-- slide 9 -->

### Wat je vooraf uitzoekt

- Welke data bewaart de app, en hoe hangt die samen?
- Wie gebruikt ze, en wie mag wat?
- Welke schermen zijn er, en wat doet een gebruiker daar?
- Met welke andere systemen praat ze: een betaalprovider, een API, een mailserver?

<!-- slide 10 -->

### Manieren om te denken

- **Wireframe**: Wat staat er op elk scherm
- **Flowchart**: Wat gebeurt er, in welke volgorde
- **User story**: Wie wil wat, en waarom
- **ERD**: Welke data, en hoe hangt ze samen

> [!TIP]
> Ze werken alleen als je ze maakt voor je bouwt. Een ERD achteraf is documentatie, geen analyse.

<!-- slide 11 -->

## 2. Wat een ERD is

<!-- slide 12 -->

### ERD

**Entity Relationship Diagram**: Een tekening van je database: de tabellen, hun kolommen, en hoe ze naar elkaar verwijzen.

- Je tekent hem voor je één tabel maakt
- Je denkt na over de structuur, zonder dat SQL in de weg zit
- Iedereen leest dezelfde notatie: je team, je docent, de developer na jou

<!-- slide 13 -->

### Je kent er al een

ERD: gyms (id PK, name, city, badge, leader_id FK); trainers (id PK, name, email, hometown, badges, created_at); catches (id PK, trainer_id FK, pokemon_id FK, level, caught_on); pokemon (id PK, name, type, type_2, hp, evolves_from FK). trainers.id 1:0..1 gyms.leader_id, trainers.id 1:n catches.trainer_id, pokemon.id 1:n catches.pokemon_id, pokemon.id 1:0..n pokemon.evolves_from.

De Pokédex van vorige week. Vier tabellen, vier relaties, en je ziet in één oogopslag wat waarnaar verwijst.

<!-- slide 14 -->

### Drie woorden

| In een ERD | In je database | Voorbeeld |
| --- | --- | --- |
| Entity | Een tabel | `trainers` |
| Attribute | Een kolom | `hometown` |
| Relationship | Een foreign key, of een tussentabel | `catches.trainer_id` |

<!-- slide 15 -->

### Kraaienpoten

1: Eén, n: Meerdere, 1: Precies één, 0..1: Nul of één, 0..n: Nul of meerdere, 1..n: Eén of meerdere

<!-- slide 16 -->

### Een webshop

![ERD van een webshop met de tabellen Customer, Order, Products, Categories en Delivery, met kraaienpoten tussen de kolommen](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/voorbeeld-webshop.webp)

<!-- slide 17 -->

### Punten op school

![ERD met Students, Groups, Subjects, Teachers, Marks en een tussentabel Subject/teacher, getekend met dubbele streepjes en kraaienpoten](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/voorbeeld-punten.svg)

<!-- slide 18 -->

### Een pronostiek voor de playoffs

![ERD van een hockeypronostiek met PlayoffRound, HockeyGame, HockeyTeam, User en de voorspellingen van elke gebruiker, met bolletjes voor nul](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/voorbeeld-hockey.svg)

<!-- slide 19 -->

### Werkvergunningen

![ERD van werkvergunningen voor een tankopslag, met een twintigtal tabellen rond permits, gekleurde lijnen per groep en gele notities met regels](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/voorbeeld-vergunningen.webp)

<!-- slide 20 -->

### Accessoires, in Lucidchart

![ERD van een webshop voor accessoires met dealers, customers, orders, suppliers en accessoires, met kolommen per taal en rode en blauwe lijnen](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/accessoires-lucidchart.webp)

<!-- slide 21 -->

### Hetzelfde project, in Azimutt

![Dezelfde accessoires-database in Azimutt, met gekleurde koppen per groep en meer kolommen, zoals slugs per taal](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/accessoires-azimutt.webp)

<!-- slide 22 -->

### Waarmee je tekent

- **Papier**: De snelste. Je eerste versie gooi je toch weg.
- **Spreadsheet**: Een blok per tabel, de kolommen eronder. Snel herschikt.
- **[dbdiagram.io](https://dbdiagram.io/)**: Je typt de tabellen, de tool tekent
- **[DBML Viewer](https://genx.tools/dbml-viewer)**: Plak je DBML, of bouw je tabellen met een editor
- **[drawSQL](https://drawsql.app/)**: Slepen en klikken, gemaakt voor databases
- **[Miro](https://miro.com/)**: Een whiteboard met ERD vormen, handig in groep
- **[Lucidchart](https://www.lucidchart.com/)**: Een tekentool voor elk soort diagram
- **[Azimutt](https://azimutt.app/)**: Leest een bestaande database in en tekent ze

<!-- slide 23 -->

**Don't block your thinking by thinking too much.**

Je eerste ERD is fout, en dat is de bedoeling. Teken, lees het voor aan je buur, en teken opnieuw.

<!-- slide 24 -->

### Op papier

![Een ERD van een lagere school, met de hand getekend in blauwe balpen: classes, teachers en children, met kraaienpoten aan class_id](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/school-op-papier.webp)

<!-- slide 25 -->

### Of in een spreadsheet

![Een eerste versie van de accessoires-database in een spreadsheet: een blok per tabel met een gele kop en de kolommen eronder, zonder lijnen](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/accessoires-spreadsheet.webp)

<!-- slide 26 -->

## 3. Entities

<!-- slide 27 -->

### Een lagere school

We maken een database voor een lagere school. De school heeft verschillende klassen. Elke klas heeft een naam, bijvoorbeeld 1A. Er zijn ook leerkrachten. Een leerkracht geeft les in één klas, maar een klas kan meerdere leerkrachten hebben, bijvoorbeeld als iemand halftijds werkt.

Van een leerkracht bewaren we de naam, de leeftijd, het gsm-nummer en de arbeidsregeling (halftijds of voltijds). En de school heeft leerlingen. Van een leerling bewaren we de naam en wanneer die in het systeem is aangemaakt.

<!-- slide 28 -->

### Entities

**Entity**: Een soort ding waar je er meerdere van hebt, en waarover je data bewaart. Wordt een tabel.

- Zoek de zelfstandige naamwoorden in de tekst
- Engels en meervoud: `teachers`, niet `leerkracht`
- Kleine letters, en een underscore tussen woorden: `order_items`
- Een naam die zegt wat erin zit. Geen `data`, geen `table1`.

<!-- slide 29 -->

### Over welke dingen gaat dit

We maken een database voor een lagere school. De school heeft verschillende klassen. Elke klas heeft een naam, bijvoorbeeld 1A. Er zijn ook leerkrachten. Een leerkracht geeft les in één klas, maar een klas kan meerdere leerkrachten hebben, bijvoorbeeld als iemand halftijds werkt.

Van een leerkracht bewaren we de naam, de leeftijd, het gsm-nummer en de arbeidsregeling (halftijds of voltijds). En de school heeft leerlingen. Van een leerling bewaren we de naam en wanneer die in het systeem is aangemaakt.

<!-- slide 30 -->

### Drie entities

ERD: classes; students; teachers.

> [!NOTE]
> De school zelf wordt geen tabel. Er is er maar één, en we bewaren er niets over.

<!-- slide 31 -->

## 4. Attributes

<!-- slide 32 -->

### Attributes

**Attribute**: Een eigenschap van een entity. Wordt een kolom, met een type.

- Engels en enkelvoud, in snake_case: `date_of_birth`
- Herhaal de tabel niet: `name`, niet `teacher_name`
- Een naam die zegt wat erin zit. Geen `nr`, geen `info`.
- Elk attribute krijgt een type, en je beslist of het leeg mag zijn

<!-- slide 33 -->

### Wat bewaren we erover

We maken een database voor een lagere school. De school heeft verschillende klassen. Elke klas heeft een naam, bijvoorbeeld 1A. Er zijn ook leerkrachten. Een leerkracht geeft les in één klas, maar een klas kan meerdere leerkrachten hebben, bijvoorbeeld als iemand halftijds werkt.

Van een leerkracht bewaren we de naam, de leeftijd, het gsm-nummer en de arbeidsregeling (halftijds of voltijds). En de school heeft leerlingen. Van een leerling bewaren we de naam en wanneer die in het systeem is aangemaakt.

<!-- slide 34 -->

### Wat de klant zegt, wat jij bewaart

| De klant zegt | Jij bewaart | Waarom |
| --- | --- | --- |
| Naam | `first_name, last_name` | Dan kan je sorteren op achternaam |
| Leeftijd | `date_of_birth DATE` | Een leeftijd is volgend jaar fout |
| Gsm-nummer | `mobile VARCHAR(20)` | Je rekent er niet mee, en 04 verliest zijn nul |
| Halftijds of voltijds | `full_time BOOLEAN` | Twee mogelijkheden, dus ja of nee |
| Wanneer aangemaakt | `created_at DATETIME` | Een datum met het uur erbij |

<!-- slide 35 -->

### Met attributes en types

ERD: classes (id PK, name); students (id PK, first_name, last_name, created_at); teachers (id PK, first_name, last_name, date_of_birth, mobile, full_time).

<!-- slide 36 -->

## 5. Relationships

<!-- slide 37 -->

### Relationships

**Relationship**: Hoe twee entities samenhangen. Wordt een foreign key, of een tussentabel.

- De foreign key: enkelvoud van de andere tabel, en `_id`
- Teken altijd het soort relatie: één of meerdere, aan beide kanten
- De lijn loopt van de primary key naar de foreign key
- Zeg elke relatie luidop, in twee richtingen, voor je ze tekent

<!-- slide 38 -->

### Hoe hangt het samen

We maken een database voor een lagere school. De school heeft verschillende klassen. Elke klas heeft een naam, bijvoorbeeld 1A. Er zijn ook leerkrachten. Een leerkracht geeft les in één klas, maar een klas kan meerdere leerkrachten hebben, bijvoorbeeld als iemand halftijds werkt.

Van een leerkracht bewaren we de naam, de leeftijd, het gsm-nummer en de arbeidsregeling (halftijds of voltijds). En de school heeft leerlingen. Van een leerling bewaren we de naam en wanneer die in het systeem is aangemaakt.

<!-- slide 39 -->

**Leerlingen zitten ook in een klas.**

Dat staat nergens in de tekst. Voor de klant is het zo vanzelfsprekend dat hij het niet zegt. Vraag door, of denk verder dan wat er gevraagd is.

<!-- slide 40 -->

### Say it like you know it

![Een man in een keuken zegt: I wanna hear it](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/i-wanna-hear-it.webp)

<!-- slide 41 -->

### Klas en leerkracht

Eén klas heeft **meerdere** leerkrachten

Eén leerkracht geeft les in **één** klas

ERD: classes (id PK, name); teachers (id PK, class_id FK, first_name, last_name, date_of_birth, mobile, full_time). classes.id 1:n teachers.class_id.

<!-- slide 42 -->

### Klas en leerling

Eén klas heeft **meerdere** leerlingen

Eén leerling zit in **één** klas

ERD: classes (id PK, name); students (id PK, class_id FK, first_name, last_name, created_at). classes.id 1:n students.class_id.

<!-- slide 43 -->

### Het ERD van de school

ERD: classes (id PK, name); students (id PK, class_id FK, first_name, last_name, created_at); teachers (id PK, class_id FK, first_name, last_name, date_of_birth, mobile, full_time). classes.id 1:n teachers.class_id, classes.id 1:n students.class_id.

<!-- slide 44 -->

![Een vrouw in een klaslokaal zegt: All you have to do is say it!](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/say-it.webp)

<!-- slide 45 -->

### Zeg het luidop

Eén klas heeft meerdere leerkrachten. Eén leerkracht geeft les in één klas. One to many.

Eén klas heeft meerdere leerlingen. Eén leerling zit in één klas. One to many.

Eén spel heeft meerdere categorieën. Eén categorie bevat meerdere spellen. Many to many.

Eén persoon heeft één paspoort. Eén paspoort is van één persoon. One to one.

<!-- slide 46 -->

### Een ERD dat iemand kan lezen

#### Doe dit

- De primary key bovenaan, de foreign keys eronder
- Tabellen die bij elkaar horen, naast elkaar
- Korte lijnen, die zo weinig mogelijk kruisen
- Bij elke kolom een type

#### Doe dit niet

- Een lijn zonder kraaienpoot: wie heeft er meerdere?
- Een lijn die ergens aan een tabel hangt, niet aan een kolom
- Nederlands en Engels door elkaar
- Een foto van een servet in je portfolio

<!-- slide 47 -->

### Werk netjes

![Een rommelig ERD: tabellen verspreid over het blad, lijnen die kruisen en over andere tabellen lopen, en kolommen als favarites_id](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/zo-niet.webp)

<!-- slide 48 -->

### Zo wel

![Het ERD van de accessoires-webshop: tabellen in kolommen, lijnen die vertrekken en aankomen bij een kolom, rood en blauw per groep](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/accessoires-lucidchart.webp)

<!-- slide 49 -->

### Een stap verder

ERD: categories (id PK, parent_category_id FK, name); category_game (id PK, category_id FK, game_id FK); games (id PK, name); owners (id PK, name); game_owner (id PK, game_id FK, owner_id FK). categories.id 1:n category_game.category_id, games.id 1:n category_game.game_id, owners.id 1:n game_owner.owner_id, games.id 1:n game_owner.game_id, categories.id 1:0..n categories.parent_category_id.

Een tussentabel heet naar de twee tabellen die ze verbindt, in het enkelvoud en alfabetisch: `category_game`.

<!-- slide 50 -->

### Een tabel die naar zichzelf verwijst

Categorieën als boom en als rijen in één tabel, met een parent_category_id die naar een andere rij verwijst: 1 Bordspellen (parent NULL), 2 Kaartspellen (parent NULL), 3 Videogames (parent NULL), 4 Puzzels (parent NULL), 5 Strategie (parent 1), 6 Familie (parent 1), 7 Coöperatief (parent 1), 8 Party (parent 1), 9 Slagenspellen (parent 2), 10 Escape room (parent 7), 11 Legacy (parent 7).

<!-- slide 51 -->

### Een film heeft meerdere acteurs, en een acteur speelt in meerdere films. Je wil ook de naam van de rol bewaren. Waar komt die?

- A. In movies, als kolom role
- B. In actors, als kolom role
- C. In de tussentabel, naast movie_id en actor_id
- D. In een eigen tabel roles, zonder foreign keys

**Antwoord:** C. In de tussentabel, naast movie_id en actor_id

De rol zegt iets over deze acteur in deze film. Dat is precies één rij van de tussentabel. In movies zou elke film maar één rol hebben, in actors speelt een acteur altijd dezelfde rol. Zo kreeg catches vorige week een level.

<!-- slide 52 -->

## 6. Normaliseren

<!-- slide 53 -->

### Eén grote tabel

| trainer | hometown | pokemon | types | level |
| --- | --- | --- | --- | --- |
| Ash | Pallet Town | Pikachu | Electric | 81 |
| Ash | Pallet Town | Charizard | Fire, Flying | 60 |
| Ash | Palet Town | Bulbasaur | Grass, Poison | 35 |
| Gary | Pallet Town | Arcanine | Fire | 61 |
| Lt. Surge | Vermilion City | Pikachu | Electric | 18 |

Elke trainer, elke hometown en elk type staat er zo vaak als er vangsten zijn. Dat heet redundantie.

<!-- slide 54 -->

### Wat er misgaat

- Aanpassen: Pallet Town krijgt een andere naam, en je past elke rij aan. Vergeet er één, en je hebt twee steden.
- Toevoegen: Sam heeft nog niets gevangen. Zonder vangst is er geen rij om hem in te zetten.
- Verwijderen: Lt. Surge laat zijn Pikachu vrij, en met die rij is Lt. Surge zelf ook weg.
- Zoeken: welke Pokémon zijn Flying? Dat wordt LIKE in een lijstje met komma’s.

<!-- slide 55 -->

**Elk feit op één plek.**

Dat is normaliseren. Je splitst tot elke tabel over één soort ding gaat, en verbindt ze met foreign keys.

<!-- slide 56 -->

### Vraag 1: staat er één waarde in elke cel

#### Niet

- Een lijstje in een cel: `Fire, Flying`
- Genummerde kolommen: `type`, `type_2`, `type_3`
- Twee dingen in één kolom: `Kerkstraat 1, 2000 Antwerpen`

#### Wel

- Een eigen tabel, met één rij per waarde
- Een tussentabel als het many to many is
- Een kolom per onderdeel: `street`, `zip_code`, `city`

<!-- slide 57 -->

### Vraag 2: waar gaat deze kolom over

| Kolom | Zegt iets over | Hoort in |
| --- | --- | --- |
| `trainer` | Wie het ving | `catches`, als `trainer_id` |
| `hometown` | De trainer, niet de vangst | `trainers` |
| `pokemon` | Wat er gevangen is | `catches`, als `pokemon_id` |
| `types` | De Pokémon, niet de vangst | `pokemon` |
| `level` | Deze vangst | `catches` |

<!-- slide 58 -->

**De key, de hele key, en niets dan de key.**

Zo vatte William Kent het in 1983 samen. Elke kolom zegt iets over de primary key van haar eigen tabel, en over niets anders.

<!-- slide 59 -->

### Wat overblijft

ERD: gyms (id PK, name, city, badge, leader_id FK); trainers (id PK, name, email, hometown, badges, created_at); catches (id PK, trainer_id FK, pokemon_id FK, level, caught_on); pokemon (id PK, name, type, type_2, hp, evolves_from FK). trainers.id 1:0..1 gyms.leader_id, trainers.id 1:n catches.trainer_id, pokemon.id 1:n catches.pokemon_id, pokemon.id 1:0..n pokemon.evolves_from.

> [!TIP]
> Wie eerst de entities zoekt en dan pas de attributes verdeelt, normaliseert zonder het woord te kennen.

<!-- slide 60 -->

### De namen

| Normaalvorm | De regel | Wat je ertegen doet |
| --- | --- | --- |
| Eerste normaalvorm1NF · first normal form | Eén waarde per cel, geen lijstjes of genummerde kolommen | Een eigen tabel met een rij per waarde |
| Tweede normaalvorm2NF · second normal form | Elke kolom hangt af van de hele primary key | Speelt alleen bij een key uit meerdere kolommen |
| Derde normaalvorm3NF · third normal form | Geen kolom hangt af van een andere gewone kolom | Wat bij een ander ding hoort, krijgt een eigen tabel |

<!-- slide 61 -->

### Tussenopdracht: Onze eigen Pokédex

*10 minuten, Per 2*

De tabel `pokemon` uit les 5. Normaliseer hem.

| id | name | type | type_2 | hp |
| --- | --- | --- | --- | --- |
| 6 | Charizard | Fire | Flying | 78 |
| 35 | Clefairy | Fairy | NULL | 70 |
| 39 | Jigglypuff | Normal | Fairy | 115 |

- Wat loopt er mis als er een Pokémon met drie types komt? En als Fairy een andere naam krijgt?
- Zeg de relatie tussen Pokémon en type in twee richtingen
- Teken het ERD dat het oplost, op papier

<!-- slide 62 -->

### Types, genormaliseerd

ERD: pokemon (id PK, name, hp); pokemon_type (id PK, pokemon_id FK, type_id FK, slot); types (id PK, name). pokemon.id 1:n pokemon_type.pokemon_id, types.id 1:n pokemon_type.type_id.

> [!NOTE]
> **De prijs**
>
> Elk feit staat op één plek, en een type hernoemen is één UPDATE. Maar het type van een Pokémon tonen kost nu twee joins.

<!-- slide 63 -->

### De factuur van vorige maand

**Normaliseren is de regel, maar er zijn uitzonderingen.** Stel: de prijs staat alleen in `products`, en een factuur rekent ze uit met een JOIN.

Northwind Traders Chai kost 18.00 in products, bestelling 32 heeft er 15 van. Factuur van bestelling 32: 15 × 18.00 = 270.00. Stijgt de prijs naar 20.00, dan rekent dezelfde factuur 300.00.

<!-- slide 64 -->

### Dubbel met een reden

**De prijs van toen**: `order_details.unit_price` naast `products.list_price`. Wat de klant toen betaalde, verandert niet mee met de prijs van vandaag.

**Een totaal dat je bewaart**: Het aantal likes bij een post, in plaats van ze bij elke pageview te tellen. Alleen als tellen te traag blijkt.

> [!TIP]
> Normaliseer eerst. Iets dubbel bewaren doe je bewust, en je kan zeggen waarom.

<!-- slide 65 -->

### Een tabel `orders` heeft de kolommen `customer_name` en `customer_email`. Wat is het probleem?

- A. Geen: zo heb je geen JOIN nodig
- B. Een klant met tien orders staat er tien keer in. Verandert zijn e-mail, dan pas je tien rijen aan.
- C. De kolomnamen zijn te lang
- D. Een e-mailadres mag niet in dezelfde tabel als een naam

**Antwoord:** B. Een klant met tien orders staat er tien keer in. Verandert zijn e-mail, dan pas je tien rijen aan.

Naam en e-mail zeggen iets over de klant, niet over het order. Ze horen in customers, en orders krijgt een customer_id. Geen JOIN nodig is waar, en precies de reden waarom iedereen deze fout één keer maakt.

<!-- slide 66 -->

## 7. Van ERD naar SQL

<!-- slide 67 -->

### Van tekening naar tabel

```sql
CREATE TABLE classes (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(10) NOT NULL,
  PRIMARY KEY (id)
);

CREATE TABLE teachers (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  class_id INT UNSIGNED NOT NULL,
  first_name VARCHAR(50) NOT NULL,
  last_name VARCHAR(50) NOT NULL,
  date_of_birth DATE NULL,
  PRIMARY KEY (id),
  FOREIGN KEY (class_id) REFERENCES classes (id)
);
```

- Eerst de tabellen waar niets naar hoeft te verwijzen
- Elke entity wordt een CREATE TABLE, elk attribute een kolom
- Elke lijn een FOREIGN KEY, in de tabel aan de kant van de kraaienpoot

<!-- slide 68 -->

### ERD als tekst

*DBML*

```text
Table classes {
  id int [pk, increment]
  name varchar(10)
}

Table teachers {
  id int [pk, increment]
  class_id int [ref: > classes.id]
  first_name varchar(50)
  last_name varchar(50)
}
```

- `pk` is de primary key, `increment` is AUTO_INCREMENT
- `ref: >` is een foreign key: meerdere leerkrachten, één klas
- Export als PNG of SVG, of als .dbml bestand

**[DBML Viewer](https://genx.tools/dbml-viewer)**: Typen links, het ERD rechts

<!-- slide 69 -->

### In je portfolio

- **DBML**: De tekst uit de viewer, in een codeblok. Wie het wil zien, plakt het in de [DBML Viewer](https://genx.tools/dbml-viewer).
- **Mermaid**: Een codeblok met `mermaid` als taal. GitHub tekent het ERD in je Markdown.
- **Screenshot**: Een export als PNG of SVG, in de map `assets`. Types en kraaienpoten moeten leesbaar zijn.

> [!TIP]
> Jij kiest. Wat er moet staan, is hetzelfde: elke tabel, elke kolom met een type, elke lijn met een kraaienpoot.

<!-- slide 70 -->

### Mermaid

*les-6.md*

````md
```mermaid
erDiagram
    classes ||--o{ teachers : heeft
    classes ||--o{ students : heeft
    teachers {
        int id PK
        int class_id FK
        varchar first_name
        varchar last_name
    }
```
````

- Een codeblok met `mermaid` als taal, en GitHub tekent het
- `||` is precies één, `o{` is nul of meerdere
- De kolommen tussen accolades, met PK en FK erachter
- De syntax staat in de [docs van Mermaid](https://mermaid.js.org/syntax/entityRelationshipDiagram.html)

<!-- slide 71 -->

## 8. Opdrachten

<!-- slide 72 -->

### Hoe je een ERD maakt

- Lees de tekst drie keer: duid de entities aan, dan de attributes, dan de zinnen over relaties
- Zeg elke relatie luidop, in twee richtingen
- Teken de tabellen, met bij elke kolom een type
- Teken de lijnen, met een kraaienpoot aan de kant van meerdere
- Controleer: staat elk feit op één plek?
- Zet de tekst met je keywords en je ERD in je portfolio

<!-- slide 73 -->

### Vul aan: Scholengroep

*15 minuten, Per 2*

Een scholengroep heeft een of meer scholen onder zich. Een school heeft klasgroepen, met daaronder cursussen. Studenten horen bij een klasgroep en schrijven zich in voor meerdere cursussen. Docenten geven les in meerdere cursussen. Studenten krijgen per semester een score voor elke cursus.

1. De negen tabellen staan op de volgende slide, de relaties niet
2. Zeg elke relatie in twee richtingen
3. Teken de lijnen, op papier of op de slide zelf
4. Kies aan elk uiteinde het juiste symbool: één of meerdere

> [!TIP]
> **Scholengroep**
>
> *Relaties*
>
> Elf lijnen. Het teken > leest als: meerdere aan de linkse kant, één aan de rechtse.
>
> ```text
> Ref: schools.parent_school_id > schools.id
> Ref: classes.school_id > schools.id
> Ref: class_student.class_id > classes.id
> Ref: class_student.student_id > students.id
> Ref: courses.class_id > classes.id
> Ref: course_student.course_id > courses.id
> Ref: course_student.student_id > students.id
> Ref: course_teacher.course_id > courses.id
> Ref: course_teacher.teacher_id > teachers.id
> Ref: evaluations.course_id > courses.id
> Ref: evaluations.student_id > students.id
> ```

<!-- slide 74 -->

### De tabellen van de scholengroep

ERD: schools (id PK, parent_school_id FK, name); classes (id PK, school_id FK, name); teachers (id PK, name, email); class_student (id PK, class_id FK, student_id FK); courses (id PK, class_id FK, name); course_teacher (id PK, course_id FK, teacher_id FK); students (id PK, name, email); course_student (id PK, course_id FK, student_id FK); evaluations (id PK, course_id FK, student_id FK, score, semester). schools.id 0..1:0..n schools.parent_school_id, schools.id 1:n classes.school_id, classes.id 1:n class_student.class_id, classes.id 1:n courses.class_id, teachers.id 1:n course_teacher.teacher_id, students.id 1:n class_student.student_id, courses.id 1:n course_student.course_id, courses.id 1:n evaluations.course_id, courses.id 1:n course_teacher.course_id, students.id 1:n course_student.student_id, students.id 1:n evaluations.student_id.

<!-- slide 75 -->

### Opdracht: Quiz

*20 minuten, Per 2*

We maken een database voor een quiz. Op de quiz zijn er verschillende teams, met verschillende deelnemers per team. We houden ook de thema's van de quiz bij. Elke deelnemer heeft zo zijn favoriete thema's.

1. Duid de keywords aan in de tekst
2. Zeg elke relatie in twee richtingen
3. Teken het ERD, met types en kraaienpoten

> [!TIP]
> **Quiz**
>
> *ERD*
>
> Eén team heeft meerdere deelnemers. Een deelnemer heeft meerdere favoriete thema’s, en een thema is de favoriet van meerdere deelnemers.
>
> ```text
> Table teams {
>   id int [pk, increment]
>   name varchar
> }
>
> Table participants {
>   id int [pk, increment]
>   team_id int [ref: > teams.id]
>   first_name varchar
>   last_name varchar
> }
>
> Table themes {
>   id int [pk, increment]
>   name varchar
> }
>
> Table participant_theme {
>   id int [pk, increment]
>   participant_id int [ref: > participants.id]
>   theme_id int [ref: > themes.id]
> }
> ```

<!-- slide 76 -->

### Opdracht: Restaurant

Een restaurant wil zijn personeel bewaren, met naam en telefoonnummer. Een shift heeft een datum, een startuur en een einduur. Per shift werken er meerdere personeelsleden, en een personeelslid kan meerdere shifts hebben. Elke tafel heeft een locatie, zoals lange bij het raam, en een aantal plaatsen. Een tafel is toegewezen aan één personeelslid, en wordt nooit door iemand anders bediend.

1. Teken het ERD, met types en kraaienpoten
2. Zet de tekst met je keywords en het ERD in je portfolio

<!-- slide 77 -->

### Opdracht: Leasing

Een leasingmaatschappij bewaart haar auto's: nummerplaat, merk, model, kleur en brandstof. Elke auto heeft één bestuurder, en een bestuurder rijdt met één auto. Van een bestuurder bewaren we naam, leeftijd, telefoonnummer en rijksregisternummer. Een auto gaat meerdere keren op onderhoud. Van een onderhoud bewaren we de dag en de kilometerstand. Een onderhoud wordt uitgevoerd door enkele techniekers, van wie we naam en werknemersnummer bewaren.

1. Teken het ERD, met types en kraaienpoten
2. Zet de tekst met je keywords en het ERD in je portfolio

<!-- slide 78 -->

![Han Solo zegt: Good luck.](https://slides.mctantwerp.be/decks/nl/programming-principles/07-erd/good-luck.webp)

## Begrippen

- **Technische analyse**: Uitzoeken wat je gaat bouwen voor je begint: welke data, welke schermen, wie wat mag. Een ERD is het deel over de data. (slide 9)
- **ERD**: Entity Relationship Diagram: een tekening van je database, met de tabellen, hun kolommen en hoe ze naar elkaar verwijzen. Je tekent hem voor je bouwt. (slide 12)
- **Entity**: Een soort ding waarover je data bewaart: klassen, leerkrachten, orders. In de database wordt een entity een tabel. (slide 26)
- **Attribute**: Een eigenschap van een entity: de naam van een klas, de geboortedatum van een leerkracht. In de database wordt een attribute een kolom. (slide 31)
- **Relationship**: Hoe twee entities samenhangen: een klas heeft meerdere leerlingen. In de database wordt een relationship een foreign key, of een tussentabel. (slide 36)
- **Kraaienpoot**: Crow’s foot notation: het einde van een lijn in een ERD zegt hoeveel. Een streepje is één, de driepoot is meerdere, een bolletje is nul. (slide 15)
- **Self referencing table**: Een tabel met een foreign key naar zichzelf, zoals parent_category_id in categories. Zo bouw je een boom: categorieën met subcategorieën. (slide 50)
- **Normaliseren**: Je tabellen zo opsplitsen dat elk feit op één plek staat. Geen lijstjes in een cel, en elke kolom zegt iets over de primary key van haar eigen tabel. (slide 55)
- **Normaalvorm**: Een trap van normaliseren: de eerste, tweede en derde normaalvorm, of 1NF, 2NF en 3NF (first, second en third normal form). De eerste: één waarde per cel. De tweede en de derde: elke kolom hangt af van de key, de hele key, en niets dan de key. (slide 60)
- **Redundantie**: Hetzelfde feit op meer dan één plek. Past iemand het op één plek aan en vergeet hij de andere, dan spreekt je database zichzelf tegen. (slide 53)
- **DBML**: Database Markup Language: je typt je tabellen en relaties als tekst, en een tool als de DBML Viewer van genx.tools tekent het ERD. (slide 68)
- **Mermaid**: Een taal om diagrammen als tekst te schrijven in Markdown. GitHub tekent een erDiagram blok vanzelf als ERD. (slide 70)

## Links en bronnen

### In de presentatie

- [dbdiagram.io](https://dbdiagram.io/) (slide 22)
- [DBML Viewer](https://genx.tools/dbml-viewer) (slide 22, slide 68, slide 69)
- [drawSQL](https://drawsql.app/) (slide 22)
- [Miro](https://miro.com/) (slide 22)
- [Lucidchart](https://www.lucidchart.com/) (slide 22)
- [Azimutt](https://azimutt.app/) (slide 22)
- [Mermaid, Entity Relationship Diagrams](https://mermaid.js.org/syntax/entityRelationshipDiagram.html) (slide 70)

### Bronnen

- [A Simple Guide to Five Normal Forms in Relational Database Theory](https://www.bkent.net/Doc/simple5.htm): William Kent, 1983. De key, de hele key, en niets dan de key.
- [GitHub Docs, Creating diagrams](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams): Mermaid in een Markdown bestand
- [DBML, Database Markup Language](https://dbml.dbdiagram.io/docs/): De taal van dbdiagram.io
- [Laravel, Eloquent Relationships](https://laravel.com/docs/eloquent-relationships): De naam van een tussentabel: enkelvoud, alfabetisch
- [MySQL 8.4 Reference Manual, FOREIGN KEY Constraints](https://dev.mysql.com/doc/refman/8.4/en/create-table-foreign-keys.html)
