casus-9-data

CASUS 9 – SPORTSCHOOL

Jesse Flantua - 101729

ONDERDEEL A: HET ONTWERP

Casus beschrijving

Een sportschool wil een systeem waarin leden, lessen, instructeurs, apparaten en persoonlijke trainingsschema’s worden bijgehouden. Van elk lid worden de naam, het lidnummer, het abonnementstype en de vervaldatum van het lidmaatschap geregistreerd. De sportschool biedt groepslessen aan, zoals spinning of yoga, met een vaste instructeur, dag en tijd. Leden kunnen zich inschrijven voor lessen en er wordt geregistreerd of ze aanwezig waren. Daarnaast kunnen leden een persoonlijk trainingsschema krijgen, waarin per oefening wordt vastgelegd op welk apparaat getraind wordt, hoeveel sets en herhalingen nodig zijn, en wat het trainingsdoel is. De sportschool wil ook de onderhoudsdata van de apparaten kunnen bijhouden. Het systeem moet overzichten kunnen genereren van actieve schema’s, bezetting van apparaten en deelname aan groepslessen.


STAP 1: DATA VERZAMELEN

In deze stap selecteren we alle zelfstandig naamwoorden, acties en tijd/plaats/hoeveelheid aanduidingen uit de casus tekst.

Zelfstandig naamwoorden

Tijd/plaats/hoeveelheid aanduidingen

Acties


STAP 2: SELECTEREN WAT WE WEL EN NIET NODIG HEBBEN

Wat we NIET opnemen

Wat we WEL opnemen

Zelfstandig naamwoorden:

Acties:


STAP 3: GROEPEREN

We groeperen nu wat bij elkaar hoort:

Lid

Instructeur

Les (groepsles)

Apparaat

Trainingsschema (persoonlijk)

Oefening (binnen trainingsschema)

Acties beschrijving

  1. Een lid kan zich inschrijven voor een groepsles
    • Relatie tussen lid en les
    • Er moet geregistreerd worden of het lid aanwezig was
  2. Een lid kan een persoonlijk trainingsschema krijgen
    • Relatie tussen lid en trainingsschema
    • Een trainingsschema bevat meerdere oefeningen
  3. Per oefening wordt vastgelegd op welk apparaat getraind wordt
    • Relatie tussen oefening en apparaat
    • Inclusief sets, herhalingen en trainingsdoel
  4. Van apparaten worden onderhoudsdata bijgehouden
    • Registratie van onderhoudsmomenten

STAP 4: ENTITEITEN BEPALEN

Op basis van stap 3 kunnen we de volgende entiteiten identificeren met hun eigenschappen:

Lid

Gemaakte keuzes:

Instructeur

Gemaakte keuzes:

Les

Gemaakte keuzes:

Apparaat

Gemaakte keuzes:

Trainingsschema

Gemaakte keuzes:

Oefening

Gemaakte keuzes:

Onderhoud

Gemaakte keuzes:


STAP 5: PRIMARY-KEY BEPALEN

We bepalen nu voor elke entiteit wat de primary-key wordt:

Lid

Keuze: Lidnummer is uniek per lid en staat al in de casus beschreven, dus is een logische primary key.

Instructeur

Keuze: Een code is geschikt als primary key omdat het kort en uniek is.

Les

Keuze: We voegen een kunstmatige ID toe omdat de combinatie van eigenschappen niet uniek genoeg is (er kunnen meerdere lessen met dezelfde naam op verschillende momenten zijn).

Apparaat

Keuze: Een kunstmatige ID is het meest praktisch omdat apparaatnamen kunnen veranderen.

Trainingsschema

Keuze: Een kunstmatige ID omdat meerdere schema’s dezelfde naam kunnen hebben.

Oefening

Keuze: Een kunstmatige ID omdat oefeningnamen kunnen veranderen of dubbelzinnig zijn.

Onderhoud

Keuze: Een kunstmatige ID omdat meerdere onderhoudsacties op dezelfde datum kunnen plaatsvinden.


STAP 6: ACTIVITEITEN VERWERKEN

Nu verwerken we de acties die we in stap 3 hebben geïdentificeerd:

Actie 1: Een lid kan zich inschrijven voor een groepsles

Dit is een veel-op-veel relatie:

Dit levert een nieuwe entiteit op: Inschrijving

Inschrijving:

Keuze:

Actie 2: Een lid kan een persoonlijk trainingsschema krijgen

Deze relatie is al verwerkt in stap 5: Trainingsschema heeft een foreign key naar Lid (lidnummer).

Actie 3: Per oefening in een trainingsschema wordt vastgelegd op welk apparaat getraind wordt

Dit levert een nieuwe entiteit op die de koppeling maakt tussen Trainingsschema en Oefening: Schema_oefening

Schema_oefening:

Keuze:

Opmerking: apparaatID zou NULL kunnen zijn voor oefeningen die geen apparaat nodig hebben (bijv. push-ups).

Actie 4: Van apparaten worden onderhoudsdata bijgehouden

Deze is al verwerkt in stap 5 met de Onderhoud entiteit.


STAP 7: ONTBREKENDE RELATIES AANBRENGEN

We controleren of alle entiteiten logische relaties hebben:

Lid - gekoppeld aan Inschrijving en Trainingsschema ✓ Instructeur - gekoppeld aan Les ✓ Les - gekoppeld aan Instructeur en Inschrijving ✓ Apparaat - gekoppeld aan Schema_oefening en Onderhoud ✓ Trainingsschema - gekoppeld aan Lid en Schema_oefening ✓ Oefening - gekoppeld aan Schema_oefening ✓ Onderhoud - gekoppeld aan Apparaat ✓ Inschrijving - koppeltabel tussen Lid en Les ✓ Schema_oefening - koppeltabel tussen Trainingsschema en Oefening

Alle entiteiten hebben logische relaties. Er zijn geen zwevende entiteiten die nergens aan gekoppeld zijn.


STAP 8: CONTROLE OP FOUTEN

Controle op redundantie

Controle 1: Instructeurnaam bij Les?

Controle 2: Lidnaam bij Inschrijving?

Controle 3: Apparaatnaam bij Schema_oefening?

Controle 4: Oefeningnaam bij Schema_oefening?

Controle op afhankelijkheid van de primary-key:

Lid tabel

Instructeur tabel

Les tabel

Apparaat tabel

Trainingsschema tabel

Oefening tabel

Onderhoud tabel

Inschrijving tabel (gecombineerde sleutel)

Schema_oefening tabel (gecombineerde sleutel)

Conclusie: Geen fouten geconstateerd. Het ontwerp is vrij van redundantie en alle eigenschappen zijn correct afhankelijk van hun primary keys.


STAP 9: DATATYPE BEPALEN

Nu bepalen we voor elke eigenschap het juiste datatype:

Lid

Instructeur

Les

Keuze voor dag: ENUM is efficiënter maar minder flexibel. VARCHAR geeft meer vrijheid voor verschillende schrijfwijzen.

Apparaat

Trainingsschema

Oefening

Onderhoud

Inschrijving

Schema_oefening


SAMENVATTING DATADICTIONARY

Lid

Kolom Datatype Key Beschrijving
lidnummer INT PK Uniek identificatienummer van het lid
naam VARCHAR(100)   Volledige naam van het lid
abonnementstype VARCHAR(50)   Type abonnement (Basis, Premium, etc.)
vervaldatum DATE   Datum waarop het abonnement verloopt

Instructeur

Kolom Datatype Key Beschrijving
instructeurcode VARCHAR(10) PK Unieke code van de instructeur
instructeurnaam VARCHAR(100)   Naam van de instructeur

Les

Kolom Datatype Key Beschrijving
lesID INT PK Uniek identificatienummer van de les
lesnaam VARCHAR(50)   Naam van de les (Spinning, Yoga, etc.)
dag VARCHAR(10)   Dag van de week waarop de les plaatsvindt
tijd TIME   Tijdstip waarop de les begint
instructeurcode VARCHAR(10) FK Code van de instructeur die de les geeft

Apparaat

Kolom Datatype Key Beschrijving
apparaatID INT PK Uniek identificatienummer van het apparaat
apparaatnaam VARCHAR(100)   Naam van het apparaat
apparaattype VARCHAR(50)   Type apparaat (Cardio, Kracht, etc.)
aanschafdatum DATE   Datum waarop het apparaat is aangeschaft

Trainingsschema

Kolom Datatype Key Beschrijving
schemaID INT PK Uniek identificatienummer van het schema
schemanaam VARCHAR(100)   Naam van het trainingsschema
startdatum DATE   Datum waarop het schema start
trainingsdoel TEXT   Algemeen doel van het trainingsschema
lidnummer INT FK Lidnummer van het lid dat dit schema heeft

Oefening

Kolom Datatype Key Beschrijving
oefeningID INT PK Uniek identificatienummer van de oefening
oefeningnaam VARCHAR(100)   Naam van de oefening
beschrijving TEXT   Uitleg hoe de oefening wordt uitgevoerd

Onderhoud

Kolom Datatype Key Beschrijving
onderhoudID INT PK Uniek identificatienummer van de onderhoudsactie
onderhoudsdatum DATE   Datum waarop het onderhoud plaatsvond
beschrijving TEXT   Beschrijving van wat er onderhouden is
apparaatID INT FK ID van het apparaat dat onderhouden is

Inschrijving

Kolom Datatype Key Beschrijving
lesID INT PK, FK ID van de les waarvoor ingeschreven is
lidnummer INT PK, FK Lidnummer van het ingeschreven lid
aanwezig BOOLEAN   Of het lid aanwezig was (1) of niet (0)
inschrijfdatum DATE   Datum waarop de inschrijving plaatsvond

Schema_oefening

Kolom Datatype Key Beschrijving
schemaID INT PK, FK ID van het trainingsschema
oefeningID INT PK, FK ID van de oefening
volgnummer INT PK Volgorde van de oefening in het schema
apparaatID INT FK ID van het te gebruiken apparaat (kan NULL)
sets INT   Aantal sets van deze oefening
herhalingen INT   Aantal herhalingen per set
oefening_doel VARCHAR(255)   Specifiek doel voor deze oefening

RELATIES OVERZICHT

  1. Lid → Trainingsschema: Een lid kan meerdere trainingsschema’s hebben (1:n)
  2. Lid → Inschrijving → Les: Een lid kan zich voor meerdere lessen inschrijven en een les kan meerdere leden hebben (n:m via Inschrijving)
  3. Instructeur → Les: Een instructeur kan meerdere lessen geven, elke les heeft één instructeur (1:n)
  4. Trainingsschema → Schema_oefening → Oefening: Een trainingsschema bevat meerdere oefeningen en een oefening kan in meerdere schema’s zitten (n:m via Schema_oefening)
  5. Apparaat → Schema_oefening: Een apparaat kan in meerdere oefeningen gebruikt worden (1:n)
  6. Apparaat → Onderhoud: Een apparaat kan meerdere onderhoudsmomenten hebben (1:n)

CONCLUSIE

Het database ontwerp voor de sportschool is nu compleet. We hebben 9 tabellen geïdentificeerd:

Alle stappen van het stappenplan zijn doorlopen en er zijn bewuste keuzes gemaakt voor datatypes en relaties. Het ontwerp is gecontroleerd op redundantie en afhankelijkheid van primary keys.

In de volgende stap (Onderdeel B) kan dit ontwerp geïmplementeerd worden in een daadwerkelijke database met alle tabellen, kolommen, datatypes en foreign key constraints.


ONDERDEEL B: IMPLEMENTATIE

Database Naam

Naam: sportschool_systeem


Overzicht Database Structuur

De database bestaat uit 9 tabellen zoals ontworpen in Onderdeel A:

  1. Lid - Bevat alle sportschool leden
  2. Instructeur - Bevat alle instructeurs die lessen geven
  3. Les - Bevat alle groepslessen die aangeboden worden
  4. Apparaat - Bevat alle fitnessapparaten
  5. Trainingsschema - Bevat persoonlijke trainingsschema’s
  6. Oefening - Bevat alle beschikbare oefeningen (herbruikbaar)
  7. Onderhoud - Registreert onderhoudsmomenten van apparaten
  8. Inschrijving - Koppeltabel tussen Lid en Les (veel-op-veel)
  9. Schema_oefening - Koppeltabel tussen Trainingsschema en Oefening (veel-op-veel)

SQL Script voor Database Aanmaken

Het volledige SQL script voor het aanmaken van de database en alle tabellen is hieronder weergegeven, je kunt dit script ook vinden in het bestand sportschool_create_database.sql.

-- ===================================
-- CASUS 9: SPORTSCHOOL DATABASE SETUP
-- ===================================
-- Dit SQL script maakt de volledige database aan voor het sportschool systeem
-- met alle tabellen, kolommen, datatypes en foreign key constraints.
--
-- Database naam: sportschool_systeem
-- ===================================

-- Database aanmaken (indien nodig, verwijder als deze al bestaat)
-- DROP DATABASE IF EXISTS sportschool_systeem;
-- CREATE DATABASE sportschool_systeem CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- USE sportschool_systeem;

-- ===================================
-- TABEL 1: Lid
-- ===================================
-- Bevat alle leden van de sportschool
CREATE TABLE Lid (
    lidnummer INT AUTO_INCREMENT PRIMARY KEY,
    naam VARCHAR(100) NOT NULL,
    abonnementstype VARCHAR(50) NOT NULL,
    vervaldatum DATE NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- TABEL 2: Instructeur
-- ===================================
-- Bevat alle instructeurs die lessen geven
CREATE TABLE Instructeur (
    instructeurcode VARCHAR(10) PRIMARY KEY,
    instructeurnaam VARCHAR(100) NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- TABEL 3: Les
-- ===================================
-- Bevat alle groepslessen die aangeboden worden
CREATE TABLE Les (
    lesID INT AUTO_INCREMENT PRIMARY KEY,
    lesnaam VARCHAR(50) NOT NULL,
    dag VARCHAR(10) NOT NULL,
    tijd TIME NOT NULL,
    instructeurcode VARCHAR(10) NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (instructeurcode) REFERENCES Instructeur(instructeurcode) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- TABEL 4: Apparaat
-- ===================================
-- Bevat alle fitnessapparaten in de sportschool
CREATE TABLE Apparaat (
    apparaatID INT AUTO_INCREMENT PRIMARY KEY,
    apparaatnaam VARCHAR(100) NOT NULL,
    apparaattype VARCHAR(50) NOT NULL,
    aanschafdatum DATE NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- TABEL 5: Trainingsschema
-- ===================================
-- Bevat persoonlijke trainingsschema's voor leden
CREATE TABLE Trainingsschema (
    schemaID INT AUTO_INCREMENT PRIMARY KEY,
    schemanaam VARCHAR(100) NOT NULL,
    startdatum DATE NOT NULL,
    trainingsdoel TEXT,
    lidnummer INT NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (lidnummer) REFERENCES Lid(lidnummer) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- TABEL 6: Oefening
-- ===================================
-- Bevat alle beschikbare oefeningen (herbruikbaar in meerdere schema's)
CREATE TABLE Oefening (
    oefeningID INT AUTO_INCREMENT PRIMARY KEY,
    oefeningnaam VARCHAR(100) NOT NULL,
    beschrijving TEXT,
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- TABEL 7: Onderhoud
-- ===================================
-- Registreert onderhoudsmomenten van apparaten
CREATE TABLE Onderhoud (
    onderhoudID INT AUTO_INCREMENT PRIMARY KEY,
    onderhoudsdatum DATE NOT NULL,
    beschrijving TEXT,
    apparaatID INT NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (apparaatID) REFERENCES Apparaat(apparaatID) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- TABEL 8: Inschrijving (Koppeltabel)
-- ===================================
-- Koppelt leden aan lessen (veel-op-veel relatie)
-- Registreert ook aanwezigheid
CREATE TABLE Inschrijving (
    lesID INT NOT NULL,
    lidnummer INT NOT NULL,
    aanwezig TINYINT(1) DEFAULT 0,
    inschrijfdatum DATE NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (lesID, lidnummer),
    FOREIGN KEY (lesID) REFERENCES Les(lesID) ON DELETE CASCADE ON UPDATE CASCADE,
    FOREIGN KEY (lidnummer) REFERENCES Lid(lidnummer) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- TABEL 9: Schema_oefening (Koppeltabel)
-- ===================================
-- Koppelt oefeningen aan trainingsschema's (veel-op-veel relatie)
-- Bevat specifieke informatie per oefening (sets, herhalingen, apparaat)
CREATE TABLE Schema_oefening (
    schemaID INT NOT NULL,
    oefeningID INT NOT NULL,
    volgnummer INT NOT NULL,
    apparaatID INT DEFAULT NULL,
    sets INT NOT NULL,
    herhalingen INT NOT NULL,
    oefening_doel VARCHAR(255),
    deleted_at DATETIME DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (schemaID, oefeningID, volgnummer),
    FOREIGN KEY (schemaID) REFERENCES Trainingsschema(schemaID) ON DELETE CASCADE ON UPDATE CASCADE,
    FOREIGN KEY (oefeningID) REFERENCES Oefening(oefeningID) ON DELETE CASCADE ON UPDATE CASCADE,
    FOREIGN KEY (apparaatID) REFERENCES Apparaat(apparaatID) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ===================================
-- INDICES VOOR BETERE PERFORMANCE
-- ===================================

-- Index op foreign keys voor snellere joins
CREATE INDEX idx_les_instructeur ON Les(instructeurcode);
CREATE INDEX idx_trainingsschema_lid ON Trainingsschema(lidnummer);
CREATE INDEX idx_onderhoud_apparaat ON Onderhoud(apparaatID);
CREATE INDEX idx_schema_oefening_apparaat ON Schema_oefening(apparaatID);

-- Index op datum velden voor snellere datum queries
CREATE INDEX idx_lid_vervaldatum ON Lid(vervaldatum);
CREATE INDEX idx_trainingsschema_startdatum ON Trainingsschema(startdatum);
CREATE INDEX idx_onderhoud_datum ON Onderhoud(onderhoudsdatum);
CREATE INDEX idx_inschrijving_datum ON Inschrijving(inschrijfdatum);

-- Index op deleted_at voor soft delete queries
CREATE INDEX idx_lid_deleted ON Lid(deleted_at);
CREATE INDEX idx_instructeur_deleted ON Instructeur(deleted_at);
CREATE INDEX idx_les_deleted ON Les(deleted_at);
CREATE INDEX idx_apparaat_deleted ON Apparaat(deleted_at);
CREATE INDEX idx_trainingsschema_deleted ON Trainingsschema(deleted_at);
CREATE INDEX idx_oefening_deleted ON Oefening(deleted_at);
CREATE INDEX idx_onderhoud_deleted ON Onderhoud(deleted_at);
CREATE INDEX idx_inschrijving_deleted ON Inschrijving(deleted_at);
CREATE INDEX idx_schema_oefening_deleted ON Schema_oefening(deleted_at);

-- ===================================
-- EINDE VAN DATABASE SETUP
-- ===================================

-- Overzicht van alle tabellen tonen
SHOW TABLES;

Implementatie Specificaties

Speciale Kolommen

Alle tabellen bevatten zoals vereist:

Technische Details

Resultaat

Om het resultaat to bekijken kan je hier de data-disctionary inzien: sportschool.pdf

Sportschool Database Diagram


ONDERDEEL C: DATABASE VULLEN

Overzicht van de Data

De database is gevuld met realistische testdata die representatief is voor een echte sportschool:

Tabel Aantal Records Type Data
Lid 30 Echte Nederlandse namen, verschillende abonnementstypen
Instructeur 22 Professionele instructeursnamen met unieke codes
Les 25 Diverse lestypen verspreid over de hele week
Apparaat 25 Realistische apparatuur (cardio, kracht, functioneel)
Oefening 30 Complete bibliotheek van standaard oefeningen
Trainingsschema 25 Gevarieerde trainingsschema’s voor verschillende doelen
Schema_oefening 60+ Koppeling tussen schema’s en oefeningen met details
Inschrijving 60+ Les-inschrijvingen met aanwezigheidsregistratie
Onderhoud 27 Onderhoudshistorie van apparaten

Totaal: 300+ records verspreid over alle tabellen.


SQL Script voor Data Vullen

Het volledige script vind je in het bestand sportschool_fill_data.sql. Hieronder een uitleg per tabel:

TABEL 1: Instructeur (22 records)

INSERT INTO Instructeur (instructeurcode, instructeurnaam) VALUES
('INST001', 'Mike van der Berg'),
('INST002', 'Lisa Janssen'),
('INST003', 'Thomas de Vries'),
-- ... meer instructeurs
('INST022', 'Melissa Koning');

Keuzes:

TABEL 2: Les (25 records)

INSERT INTO Les (lesnaam, dag, tijd, instructeurcode) VALUES
('Spinning Advanced', 'Maandag', '18:00:00', 'INST001'),
('Yoga Beginners', 'Maandag', '19:00:00', 'INST002'),
('CrossFit', 'Maandag', '20:00:00', 'INST003'),
-- ... meer lessen
('Circuit Training', 'Woensdag', '20:00:00', 'INST019');

Keuzes:

TABEL 3: Lid (30 records)

INSERT INTO Lid (naam, abonnementstype, vervaldatum) VALUES
('Jan Petersen', 'Premium', '2026-12-31'),
('Maria van Damme', 'Basis', '2026-06-30'),
('Peter de Koning', 'Premium', '2026-08-15'),
-- ... meer leden
('Tessa Lindeman', 'Premium', '2027-03-15');

(geen “klant1, klant2” maar echte namen):

Tabel Aantal Records Voorbeeld Data
Lid 30 Jan Petersen, Maria van Damme, Peter de Koning…
Instructeur 22 Mike van der Berg, Lisa Janssen, Thomas de Vries…
Les 25 Spinning Advanced, Yoga Beginners, CrossFit…
Apparaat 25 Loopband 1, Crosstrainer 1, Squat Rack 1…
Oefening 30 Bankdrukken, Squats, Deadlift, Pull-ups…
Trainingsschema 25 Kracht Opbouw Basis, Cardio Intensief…
Schema_oefening 60+ Koppelingen met sets, herhalingen, doelen
Inschrijving 60+ Les-inschrijvingen met aanwezigheid (1/0)
Onderhoud 27 Onderhoudsacties met beschrijvingen

Totaal: 300+ records


Kenmerken van de Data

Realistische Nederlandse namen - Geen generieke namen
Variatie in data - Verschillende abonnementstypen, lestijden, etc.
Logische koppelingen - Leden ingeschreven voor passende lessen
Complete relaties - Alle foreign keys correct ingevuld
Historische data - Onderhoud en inschrijvingen met verschillende datums
Mix van situaties - Aanwezige en afwezige leden, verschillende schema’s JOIN Instructeur i ON l.instructeurcode = i.instructeurcode;

– Toon aantal inschrijvingen per les

SELECT l.lesnaam, COUNT(\*) as aantal_inschrijvingen
FROM Les l
LEFT JOIN Inschrijving ins ON l.lesID = ins.lesID
GROUP BY l.lesID, l.lesnaam
ORDER BY aantal_inschrijvingen DESC;

Belangrijke Opmerkingen

  1. Volgorde van Uitvoeren: Voer eerst sportschool_database.sql uit en daarna pas sportschool_fill_data.sql. De data kan niet ingevoerd worden als de tabellen nog niet bestaan.

  2. Foreign Keys: Door de foreign key constraints moet de data in de juiste volgorde ingevoerd worden:
    • Eerst parent tabellen (Lid, Instructeur, Apparaat, Oefening)
    • Daarna child tabellen (Les, Trainingsschema)
    • Als laatste de koppeltabellen (Inschrijving, Schema_oefening, Onderhoud)
  3. Timestamps: De created_at en updated_at velden worden automatisch ingevuld door MySQL, deze hoef je niet handmatig in te vullen.

  4. Soft Deletes: Alle deleted_at velden zijn standaard NULL, wat betekent dat alle records actief zijn.

Verificatie

Het fill script bevat aan het einde een verificatie query die controleert of alle tabellen ≥20 records bevatten:

SELECT 'Lid' as Tabel, COUNT(*) as Aantal FROM Lid
UNION ALL SELECT 'Instructeur', COUNT(*) FROM Instructeur
-- ... (zie sportschool_fill_data.sql voor volledige query)

ONDERDEEL D: DATABASE GEBRUIKEN

In dit onderdeel worden verschillende SQL queries uitgevoerd op de sportschool database. Voor elke query wordt een beschrijving gegeven, het SQL-commando en een verwijzing naar het resultaat screenshot.


VRAAG D1: INSERT met created_at validatie

Opdracht: Geef van twee tabellen het SQL-commando waarmee een volledig extra record in de tabel ingevoerd wordt. Laat in het resultaat zien dat de kolom created_at de juiste waarde heeft.

Query D1.1 - Nieuw lid toevoegen

De vraag/opdracht aan de database:
Voeg een nieuw lid toe aan de database en toon dat created_at automatisch is ingevuld.

SQL-commando:

-- Record invoegen
INSERT INTO Lid (naam, abonnementstype, vervaldatum)
VALUES ('Sophie Verstappen', 'Premium', '2027-06-30');

-- Resultaat tonen met created_at
SELECT lidnummer, naam, abonnementstype, vervaldatum, created_at, updated_at
FROM Lid
WHERE naam = 'Sophie Verstappen';

Resultaat: Screenshot D1.1 resultaat


Query D1.2 - Nieuwe oefening toevoegen

De vraag/opdracht aan de database:
Voeg een nieuwe oefening toe aan de database en toon dat created_at automatisch is ingevuld.

SQL-commando:

-- Record invoegen
INSERT INTO Oefening (oefeningnaam, beschrijving)
VALUES ('Plank to Push-up', 'Begin in plank positie en wissel af tussen plank en push-up positie');

-- Resultaat tonen met created_at
SELECT oefeningID, oefeningnaam, beschrijving, created_at, updated_at
FROM Oefening
WHERE oefeningnaam = 'Plank to Push-up';

Resultaat: Screenshot D1.2 resultaat


VRAAG D2: UPDATE met updated_at validatie

Opdracht: Geef van twee tabellen het SQL-commando waarmee minimaal twee velden (per commando) in de database aangepast worden. Laat in het resultaat zien dat de kolom created_at en de kolom updated_at de juiste waarde heeft.

Query D2.1 - Lid gegevens aanpassen

De vraag/opdracht aan de database:
Pas het abonnementstype en de vervaldatum van een lid aan en toon dat updated_at automatisch is bijgewerkt.

SQL-commando:

-- Eerst oude waarden tonen
SELECT lidnummer, naam, abonnementstype, vervaldatum, created_at, updated_at
FROM Lid
WHERE lidnummer = 1;

-- Update uitvoeren
UPDATE Lid
SET abonnementstype = 'Student',
    vervaldatum = '2026-08-31'
WHERE lidnummer = 1;

-- Nieuwe waarden tonen (created_at ongewijzigd, updated_at bijgewerkt)
SELECT lidnummer, naam, abonnementstype, vervaldatum, created_at, updated_at
FROM Lid
WHERE lidnummer = 1;

Resultaat: Screenshot D2.1 resultaat


Query D2.2 - Apparaat gegevens aanpassen

De vraag/opdracht aan de database:
Pas de naam en het type van een apparaat aan en toon dat updated_at automatisch is bijgewerkt.

SQL-commando:

-- Eerst oude waarden tonen
SELECT apparaatID, apparaatnaam, apparaattype, aanschafdatum, created_at, updated_at
FROM Apparaat
WHERE apparaatID = 1;

-- Update uitvoeren
UPDATE Apparaat
SET apparaatnaam = 'Loopband Premium 1',
    apparaattype = 'Cardio Premium'
WHERE apparaatID = 1;

-- Nieuwe waarden tonen (created_at ongewijzigd, updated_at bijgewerkt)
SELECT apparaatID, apparaatnaam, apparaattype, aanschafdatum, created_at, updated_at
FROM Apparaat
WHERE apparaatID = 1;

Resultaat: Screenshot D2.2 resultaat


VRAAG D3: Soft Delete met deleted_at validatie

Opdracht: Geef van een tabel het SQL-commando waarmee een bepaald record gemarkeerd wordt als deleted (maar niet definitief verwijderd is). Laat zien dat de kolom deleted_at de juiste waarde heeft.

Query D3 - Les markeren als verwijderd

De vraag/opdracht aan de database:
Markeer een les als verwijderd (soft delete) en toon dat deleted_at is ingevuld met de huidige datum/tijd.

SQL-commando:

-- Eerst oude waarden tonen (deleted_at is NULL)
SELECT lesID, lesnaam, dag, tijd, deleted_at
FROM Les
WHERE lesID = 5;

-- Soft delete uitvoeren
UPDATE Les
SET deleted_at = NOW()
WHERE lesID = 5;

-- Nieuwe waarden tonen (deleted_at is nu ingevuld)
SELECT lesID, lesnaam, dag, tijd, deleted_at
FROM Les
WHERE lesID = 5;

Resultaat: Screenshot D3 resultaat


VRAAG D4: Data vergelijken binnen dezelfde tabel

Opdracht: Verzin een query die data uit dezelfde tabel met elkaar vergelijkt.

Query D4 - Leden met eerder verlopend abonnement dan referentie-lid

De vraag/opdracht aan de database:
Toon alle leden waarvan het abonnement eerder verloopt dan het abonnement van ‘Jan Petersen’.

SQL-commando:

SELECT l1.lidnummer, l1.naam, l1.abonnementstype, l1.vervaldatum
FROM Lid l1
WHERE l1.vervaldatum < (
    SELECT l2.vervaldatum
    FROM Lid l2
    WHERE l2.naam = 'Jan Petersen'
)
AND l1.deleted_at IS NULL
ORDER BY l1.vervaldatum;

Resultaat: Screenshot D4 resultaat


VRAAG D5: JOIN zonder subquery (2 queries)

Opdracht: Verzin twee query’s die data uit meerdere tabellen haalt en geen subquery gebruikt wordt.

Query D5.1 - Lessen met instructeursnamen

De vraag/opdracht aan de database:
Toon alle lessen met de naam van de instructeur die de les geeft, gesorteerd op dag en tijd.

SQL-commando:

SELECT
    l.lesnaam,
    l.dag,
    l.tijd,
    i.instructeurnaam
FROM Les l
INNER JOIN Instructeur i ON l.instructeurcode = i.instructeurcode
WHERE l.deleted_at IS NULL
ORDER BY
    FIELD(l.dag, 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag', 'Zondag'),
    l.tijd;

Resultaat: Screenshot D5.1 resultaat


Query D5.2 - Leden met hun trainingsschema’s

De vraag/opdracht aan de database:
Toon alle leden met hun trainingsschema’s, inclusief schemanaam en trainingsdoel.

SQL-commando:

SELECT
    l.lidnummer,
    l.naam AS lidnaam,
    l.abonnementstype,
    t.schemanaam,
    t.startdatum,
    t.trainingsdoel
FROM Lid l
INNER JOIN Trainingsschema t ON l.lidnummer = t.lidnummer
WHERE l.deleted_at IS NULL AND t.deleted_at IS NULL
ORDER BY l.naam, t.startdatum DESC;

Resultaat: Screenshot D5.2 resultaat


VRAAG D6: JOIN met één subquery (2 queries)

Opdracht: Verzin twee query’s die data uit meerdere tabellen haalt, waarbij een subquery gebruikt wordt.

Query D6.1 - Lessen met bovengemiddelde inschrijvingen

De vraag/opdracht aan de database:
Toon alle lessen die meer inschrijvingen hebben dan het gemiddelde aantal inschrijvingen per les.

SQL-commando:

SELECT
    l.lesnaam,
    l.dag,
    l.tijd,
    i.instructeurnaam,
    COUNT(ins.lidnummer) AS aantal_inschrijvingen
FROM Les l
INNER JOIN Instructeur i ON l.instructeurcode = i.instructeurcode
LEFT JOIN Inschrijving ins ON l.lesID = ins.lesID AND ins.deleted_at IS NULL
WHERE l.deleted_at IS NULL
GROUP BY l.lesID, l.lesnaam, l.dag, l.tijd, i.instructeurnaam
HAVING COUNT(ins.lidnummer) > (
    SELECT AVG(inschrijving_count)
    FROM (
        SELECT COUNT(*) AS inschrijving_count
        FROM Inschrijving
        WHERE deleted_at IS NULL
        GROUP BY lesID
    ) AS gemiddelde
)
ORDER BY aantal_inschrijvingen DESC;

Resultaat: Screenshot D6.1 resultaat


Query D6.2 - Leden met recent verlopen abonnement

De vraag/opdracht aan de database:
Toon leden waarvan het abonnement is verlopen en die trainingsschema’s hebben die gestart zijn na de startdatum van het oudste actieve schema.

SQL-commando:

SELECT
    l.lidnummer,
    l.naam,
    l.abonnementstype,
    l.vervaldatum,
    t.schemanaam,
    t.startdatum
FROM Lid l
INNER JOIN Trainingsschema t ON l.lidnummer = t.lidnummer
WHERE l.vervaldatum < CURDATE()
AND t.startdatum > (
    SELECT MIN(startdatum)
    FROM Trainingsschema
    WHERE deleted_at IS NULL
)
AND l.deleted_at IS NULL AND t.deleted_at IS NULL
ORDER BY l.vervaldatum DESC;

Resultaat: Screenshot D6.2 resultaat


VRAAG D7: JOIN met meerdere subqueries (2 queries)

Opdracht: Verzin twee query’s die data uit meerdere tabellen haalt, waarbij meer dan één subquery gebruikt wordt.

Query D7.1 - Apparaten met hoog gebruik en recent onderhoud

De vraag/opdracht aan de database:
Toon apparaten die in meer schema-oefeningen gebruikt worden dan gemiddeld EN waarvan het laatste onderhoud recenter is dan de gemiddelde onderhoudsdatum.

SQL-commando:

SELECT
    a.apparaatnaam,
    a.apparaattype,
    COUNT(DISTINCT so.schemaID) AS aantal_schema_gebruikt,
    MAX(o.onderhoudsdatum) AS laatste_onderhoud
FROM Apparaat a
LEFT JOIN Schema_oefening so ON a.apparaatID = so.apparaatID AND so.deleted_at IS NULL
LEFT JOIN Onderhoud o ON a.apparaatID = o.apparaatID AND o.deleted_at IS NULL
WHERE a.deleted_at IS NULL
GROUP BY a.apparaatID, a.apparaatnaam, a.apparaattype
HAVING
    COUNT(DISTINCT so.schemaID) > (
        SELECT AVG(schema_count)
        FROM (
            SELECT COUNT(DISTINCT schemaID) AS schema_count
            FROM Schema_oefening
            WHERE apparaatID IS NOT NULL AND deleted_at IS NULL
            GROUP BY apparaatID
        ) AS gemiddeld_gebruik
    )
    AND MAX(o.onderhoudsdatum) > (
        SELECT AVG(onderhoudsdatum)
        FROM Onderhoud
        WHERE deleted_at IS NULL
    )
ORDER BY aantal_schema_gebruikt DESC;

Resultaat: Screenshot D7.1 resultaat


Query D7.2 - Actieve leden met bovengemiddelde les-aanwezigheid

De vraag/opdracht aan de database:
Toon leden die vaker aanwezig zijn bij lessen dan gemiddeld EN waarvan het abonnement later verloopt dan de gemiddelde vervaldatum.

SQL-commando:

SELECT
    l.lidnummer,
    l.naam,
    l.abonnementstype,
    l.vervaldatum,
    COUNT(ins.lesID) AS totaal_inschrijvingen,
    SUM(CASE WHEN ins.aanwezig = 1 THEN 1 ELSE 0 END) AS keer_aanwezig,
    ROUND(SUM(CASE WHEN ins.aanwezig = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(ins.lesID), 2) AS aanwezigheidspercentage
FROM Lid l
INNER JOIN Inschrijving ins ON l.lidnummer = ins.lidnummer AND ins.deleted_at IS NULL
WHERE l.deleted_at IS NULL
GROUP BY l.lidnummer, l.naam, l.abonnementstype, l.vervaldatum
HAVING
    SUM(CASE WHEN ins.aanwezig = 1 THEN 1 ELSE 0 END) > (
        SELECT AVG(aanwezig_count)
        FROM (
            SELECT SUM(CASE WHEN aanwezig = 1 THEN 1 ELSE 0 END) AS aanwezig_count
            FROM Inschrijving
            WHERE deleted_at IS NULL
            GROUP BY lidnummer
        ) AS gemiddelde_aanwezigheid
    )
    AND l.vervaldatum > (
        SELECT AVG(vervaldatum)
        FROM Lid
        WHERE deleted_at IS NULL
    )
ORDER BY aanwezigheidspercentage DESC;

Resultaat: Screenshot D7.2 resultaat


VRAAG D8: JOIN met minimaal drie tabellen (2 queries)

Opdracht: Verzin twee query’s die data uit minimaal drie tabellen gebruikt.

Query D8.1 - Complete les-informatie met inschrijvingen

De vraag/opdracht aan de database:
Toon een overzicht van lessen met instructeur, ingeschreven leden en hun aanwezigheid.

SQL-commando:

SELECT
    l.lesnaam,
    l.dag,
    l.tijd,
    i.instructeurnaam,
    lid.naam AS lidnaam,
    lid.abonnementstype,
    CASE
        WHEN ins.aanwezig = 1 THEN 'Aanwezig'
        ELSE 'Afwezig'
    END AS status
FROM Les l
INNER JOIN Instructeur i ON l.instructeurcode = i.instructeurcode
INNER JOIN Inschrijving ins ON l.lesID = ins.lesID
INNER JOIN Lid lid ON ins.lidnummer = lid.lidnummer
WHERE l.deleted_at IS NULL
    AND ins.deleted_at IS NULL
    AND lid.deleted_at IS NULL
ORDER BY l.dag, l.tijd, lid.naam;

Resultaat: Screenshot D8.1 resultaat


Query D8.2 - Trainingsschema’s met oefeningen en apparaten

De vraag/opdracht aan de database:
Toon een overzicht van trainingsschema’s met de bijbehorende oefeningen en benodigde apparaten.

SQL-commando:

SELECT
    l.naam AS lidnaam,
    t.schemanaam,
    t.trainingsdoel,
    o.oefeningnaam,
    so.sets,
    so.herhalingen,
    COALESCE(a.apparaatnaam, 'Geen apparaat') AS apparaatnaam,
    so.oefening_doel
FROM Lid l
INNER JOIN Trainingsschema t ON l.lidnummer = t.lidnummer
INNER JOIN Schema_oefening so ON t.schemaID = so.schemaID
INNER JOIN Oefening o ON so.oefeningID = o.oefeningID
LEFT JOIN Apparaat a ON so.apparaatID = a.apparaatID
WHERE l.deleted_at IS NULL
    AND t.deleted_at IS NULL
    AND so.deleted_at IS NULL
    AND o.deleted_at IS NULL
ORDER BY l.naam, t.schemanaam, so.volgnummer;

Resultaat: Screenshot D8.2 resultaat


VRAAG D9: GROUP BY zonder HAVING (2 queries)

Opdracht: Verzin twee query’s die een GROUP BY gebruiken (zonder having component).

Query D9.1 - Aantal lessen per instructeur

De vraag/opdracht aan de database:
Toon per instructeur hoeveel lessen hij/zij geeft, gesorteerd op aantal lessen.

SQL-commando:

SELECT
    i.instructeurnaam,
    COUNT(l.lesID) AS aantal_lessen,
    GROUP_CONCAT(DISTINCT l.lesnaam ORDER BY l.lesnaam SEPARATOR ', ') AS gegeven_lessen
FROM Instructeur i
LEFT JOIN Les l ON i.instructeurcode = l.instructeurcode AND l.deleted_at IS NULL
WHERE i.deleted_at IS NULL
GROUP BY i.instructeurcode, i.instructeurnaam
ORDER BY aantal_lessen DESC, i.instructeurnaam;

Resultaat: Screenshot D9.1 resultaat


Query D9.2 - Aantal trainingsschema’s per abonnementstype

De vraag/opdracht aan de database:
Toon per abonnementstype hoeveel trainingsschema’s er actief zijn.

SQL-commando:

SELECT
    l.abonnementstype,
    COUNT(t.schemaID) AS aantal_schemas,
    COUNT(DISTINCT l.lidnummer) AS aantal_leden
FROM Lid l
LEFT JOIN Trainingsschema t ON l.lidnummer = t.lidnummer AND t.deleted_at IS NULL
WHERE l.deleted_at IS NULL
GROUP BY l.abonnementstype
ORDER BY aantal_schemas DESC;

Resultaat: Screenshot D9.2 resultaat


VRAAG D10: GROUP BY met HAVING (2 queries)

Opdracht: Verzin twee query’s die een GROUP BY gebruiken met een HAVING component.

Query D10.1 - Lessen met minimaal 3 inschrijvingen

De vraag/opdracht aan de database:
Toon alleen lessen die minimaal 3 inschrijvingen hebben, met het totaal aantal inschrijvingen en aanwezigen.

SQL-commando:

SELECT
    l.lesnaam,
    l.dag,
    l.tijd,
    i.instructeurnaam,
    COUNT(ins.lidnummer) AS totaal_inschrijvingen,
    SUM(CASE WHEN ins.aanwezig = 1 THEN 1 ELSE 0 END) AS aantal_aanwezig,
    ROUND(SUM(CASE WHEN ins.aanwezig = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(ins.lidnummer), 2) AS aanwezigheidspercentage
FROM Les l
INNER JOIN Instructeur i ON l.instructeurcode = i.instructeurcode
LEFT JOIN Inschrijving ins ON l.lesID = ins.lesID AND ins.deleted_at IS NULL
WHERE l.deleted_at IS NULL
GROUP BY l.lesID, l.lesnaam, l.dag, l.tijd, i.instructeurnaam
HAVING COUNT(ins.lidnummer) >= 3
ORDER BY totaal_inschrijvingen DESC;

Resultaat: Screenshot D10.1 resultaat


Query D10.2 - Apparaten met meer dan 2 onderhoudsbeurten

De vraag/opdracht aan de database:
Toon alleen apparaten die meer dan 2 keer onderhoud hebben gehad, met details over laatste onderhoud.

SQL-commando:

SELECT
    a.apparaatnaam,
    a.apparaattype,
    COUNT(o.onderhoudID) AS aantal_onderhoudsbeurten,
    MIN(o.onderhoudsdatum) AS eerste_onderhoud,
    MAX(o.onderhoudsdatum) AS laatste_onderhoud,
    DATEDIFF(MAX(o.onderhoudsdatum), MIN(o.onderhoudsdatum)) AS dagen_tussen_eerste_laatste
FROM Apparaat a
INNER JOIN Onderhoud o ON a.apparaatID = o.apparaatID
WHERE a.deleted_at IS NULL AND o.deleted_at IS NULL
GROUP BY a.apparaatID, a.apparaatnaam, a.apparaattype
HAVING COUNT(o.onderhoudID) > 2
ORDER BY aantal_onderhoudsbeurten DESC;

Resultaat: Screenshot D10.2 resultaat


VRAAG D11: Query met meer dan drie tabellen

Opdracht: Verzin een query die uit meer dan drie tabellen informatie ophaalt.

Query D11 - Compleet overzicht trainingsactiviteiten per lid

De vraag/opdracht aan de database:
Toon een compleet overzicht per lid met hun trainingsschema’s, oefeningen, apparaten, EN hun les-inschrijvingen met instructeurs. Dit geeft een volledig beeld van alle trainingsactiviteiten.

SQL-commando:

SELECT
    l.lidnummer,
    l.naam AS lidnaam,
    l.abonnementstype,
    l.vervaldatum,
    'Trainingsschema' AS activiteit_type,
    t.schemanaam AS activiteit_naam,
    o.oefeningnaam AS detail,
    CONCAT(so.sets, ' sets x ', so.herhalingen, ' herhalingen') AS specificatie,
    COALESCE(a.apparaatnaam, 'Geen apparaat') AS hulpmiddel,
    NULL AS dag_tijd
FROM Lid l
INNER JOIN Trainingsschema t ON l.lidnummer = t.lidnummer
INNER JOIN Schema_oefening so ON t.schemaID = so.schemaID
INNER JOIN Oefening o ON so.oefeningID = o.oefeningID
LEFT JOIN Apparaat a ON so.apparaatID = a.apparaatID
WHERE l.deleted_at IS NULL
    AND t.deleted_at IS NULL
    AND so.deleted_at IS NULL

UNION ALL

SELECT
    l.lidnummer,
    l.naam AS lidnaam,
    l.abonnementstype,
    l.vervaldatum,
    'Groepsles' AS activiteit_type,
    les.lesnaam AS activiteit_naam,
    i.instructeurnaam AS detail,
    CASE WHEN ins.aanwezig = 1 THEN 'Aanwezig' ELSE 'Afwezig' END AS specificatie,
    CONCAT(les.dag, ' ', les.tijd) AS hulpmiddel,
    CONCAT(les.dag, ' om ', TIME_FORMAT(les.tijd, '%H:%i')) AS dag_tijd
FROM Lid l
INNER JOIN Inschrijving ins ON l.lidnummer = ins.lidnummer
INNER JOIN Les les ON ins.lesID = les.lesID
INNER JOIN Instructeur i ON les.instructeurcode = i.instructeurcode
WHERE l.deleted_at IS NULL
    AND ins.deleted_at IS NULL
    AND les.deleted_at IS NULL

ORDER BY lidnummer, activiteit_type, activiteit_naam;

Resultaat: Screenshot D11 resultaat

Toelichting: Deze query combineert data uit 5-6 tabellen in één overzicht:

Dit geeft een volledig beeld van alle trainingsactiviteiten van leden, zowel persoonlijke schema’s als groepslessen.


SAMENVATTING

D1: 2 INSERT queries met created_at validatie
D2: 2 UPDATE queries met updated_at validatie
D3: 1 Soft DELETE query met deleted_at validatie
D4: 1 Self-join vergelijkingsquery
D5: 2 JOIN queries zonder subquery
D6: 2 JOIN queries met één subquery
D7: 2 JOIN queries met meerdere subqueries
D8: 2 JOIN queries met minimaal 3 tabellen
D9: 2 GROUP BY queries zonder HAVING
D10: 2 GROUP BY queries met HAVING
D11: 1 Complexe query met 5+ tabellen