====== LU08A1: Fremdschlüssel per ALTER TABLE + DML üben ====== **Ziel der Übung:** Ihr greift euren **LU05-Case** wieder auf (z. B. //Tierheim//, //Postzustellung//, //Online-Kleidershop//, //Eishockeyverein//, //ÖV//, //Schweizer Regionen//). Damals habt ihr Tabellen **ohne** Fremdschlüssel erstellt und Daten via WebStorm-Interface eingefügt. Jetzt - ergänzt ihr **Fremdschlüssel mit ALTER TABLE**, - testet die **Referenzaktionen** (Standard: RESTRICT), - übt **DML** (INSERT, UPDATE, DELETE) aus LU07. Arbeitsform: **2er-Gruppen** · Zeit: 1–2 Lektionen · Hilfsmittel: Kursunterlagen LU07/LU08 · Abgabe: siehe unten ==== 1) Ausgangsbasis ==== * Öffnet eure **LU05-Datenbank** in WebStorm/MySQL. * Prüft, dass **Primärschlüssel vorhanden** sind (wie in LU05), aber **keine** Fremdschlüssel. * Falls ihr neu starten wollt: erstellt die Tabellen nochmals wie in LU05 (PK ja, FK noch nicht) und füllt ein paar **Beispieldaten** ein (mind. 3 Zeilen pro Tabelle). ==== 2) Fremdschlüssel mit ALTER TABLE hinzufügen ==== Analysiert euer ERD (Crow’s Foot aus LU05). Legt für **jede 1:N-Beziehung** den Fremdschlüssel fest: Beispiele: * **Tierheim:** //tier.art_id// → //tierart.art_id//, //tier.gehege_id// → //gehege.gehege_id// * **Postzustellung:** //briefkasten.gebiet_id// → //postgebiet.gebiet_id//, //poestler.gebiet_id// → //postgebiet.gebiet_id// * **Online-Kleidershop:** //kleidungsstueck.kategorie_id// → //kategorie.kategorie_id//, //kleidungsstueck.marke_id// → //marke.marke_id// * **Eishockeyverein:** //block.team_id// → //team.team_id//, //spieler.block_id// → //block.block_id// * **ÖV:** //haltestelle.linie_id// → //linie.linie_id//, //fahrt.linie_id// → //linie.linie_id// * **Schweizer Regionen:** //gemeinde.kanton_code// → //kanton.kanton_code//, //gemeinde.kva_id// → //kva.anlage_id// **SQL-Muster (anpassen):** ALTER TABLE ADD CONSTRAINT fk__ FOREIGN KEY () REFERENCES () ON DELETE RESTRICT ON UPDATE RESTRICT; **Hinweise:** * Die **Eltern-Tabelle** (referenzierte Tabelle) muss **vorher existieren**. * Der **Datentyp** der FK-Spalte muss zum PK der Eltern passen (z. B. beide ''INT''). * ''RESTRICT'' ist ein sicherer Standard: Löschen/Ändern der Elternzeile ist **verboten**, solange Kindzeilen darauf zeigen. ==== 3) DML testen – RESTRICT erfahrbar machen ==== //DML = Data Manipulation Language -> Daten einfügen, ändern, löschen// **So gehen Sie vor (für euren gewählten Case):** 1. **Daten anlegen** 2. **Tests A–D** ausführen und das Verhalten beobachten (Kommentar sagt, was passieren soll). > Hinweis: Falls eure Tabellennamen und Spaltennamen abweichen, dann müsst ihr das entsprechend ändern. Also entweder die hier erwähnten Codesnippets anpassen oder die Spaltennamen bei euch ändern. === Case: Tierheim (Tierart → viele Tiere) == **FK:** //tier.art_id// → //tierart.art_id// **Daten** INSERT INTO tierart (art_id, bezeichnung) VALUES (1,'Hund'), (2,'Katze'); INSERT INTO tier (tier_id, name, geburtsdatum, geschlecht, art_id) VALUES (101,'Luna','2022-05-10','w',1); -- Kind zeigt auf Hund (1) **Tests A–D** -- A) INSERT: gültig vs. ungültig INSERT INTO tier (tier_id, name, geburtsdatum, geschlecht, art_id) VALUES (102,'Milo','2023-01-03','m',2); -- ok (Katze existiert) INSERT INTO tier (tier_id, name, geburtsdatum, geschlecht, art_id) VALUES (103,'Nala','2023-03-01','w',9999); -- sollte scheitern (FK!) -- B) DELETE Eltern mit Kind DELETE FROM tierart WHERE art_id = 1; -- sollte scheitern (RESTRICT), weil Tier 101 darauf zeigt -- C) UPDATE FK im Kind (umhängen) UPDATE tier SET art_id = 2 WHERE tier_id = 101; -- ok (Katze existiert jetzt) -- D) UPDATE PK in Eltern UPDATE tierart SET art_id = 5 WHERE art_id = 2; -- sollte scheitern, solange Tiere auf 2 verweisen === Case: Postzustellung (Postgebiet → viele Briefkästen) === **FK:** //briefkasten.gebiet_id// → //postgebiet.gebiet_id// **Daten** INSERT INTO postgebiet (gebiet_id, name, plz_bereich) VALUES (10,'Stadt Nord','8000-8099'), (20,'Stadt Süd','8100-8199'); INSERT INTO briefkasten (briefkasten_id, standort, leerungszeit, gebiet_id) VALUES (501,'Bahnhofplatz','18:00',10); -- Kind zeigt auf Gebiet 10 **Tests A–D** -- A) INSERT INSERT INTO briefkasten (briefkasten_id, standort, leerungszeit, gebiet_id) VALUES (502,'Zentrum','17:30',20); -- ok INSERT INTO briefkasten (briefkasten_id, standort, leerungszeit, gebiet_id) VALUES (503,'Park','17:00',9999); -- FK-Fehler erwartet -- B) DELETE Eltern DELETE FROM postgebiet WHERE gebiet_id = 10; -- scheitert (RESTRICT), weil BK 501 darauf zeigt -- C) UPDATE FK im Kind UPDATE briefkasten SET gebiet_id = 20 WHERE briefkasten_id = 501; -- ok (Gebiet 20 existiert) -- D) UPDATE PK in Eltern UPDATE postgebiet SET gebiet_id = 11 WHERE gebiet_id = 20; -- scheitert, solange Kinder auf 20 zeigen === Case: Online-Kleidershop (Kategorie → viele Kleidungsstücke) === **FK:** //kleidungsstueck.kategorie_id// → //kategorie.kategorie_id// **Daten** INSERT INTO kategorie (kategorie_id, name) VALUES (1,'Schuhe'), (2,'T-Shirts'); INSERT INTO kleidungsstueck (artikel_id, name, preis, groesse, zielgruppe, kategorie_id) VALUES (9001,'City Sneaker',89.90,'42','Herren',1); **Tests A–D** -- A) INSERT INSERT INTO kleidungsstueck (artikel_id, name, preis, groesse, zielgruppe, kategorie_id) VALUES (9002,'Basic Tee',19.90,'M','Damen',2); -- ok INSERT INTO kleidungsstueck (artikel_id, name, preis, groesse, zielgruppe, kategorie_id) VALUES (9003,'Ghost Item',9.90,'S','Damen',9999); -- FK-Fehler -- B) DELETE Eltern DELETE FROM kategorie WHERE kategorie_id = 1; -- scheitert (RESTRICT), Schuh 9001 existiert -- C) UPDATE FK im Kind UPDATE kleidungsstueck SET kategorie_id = 2 WHERE artikel_id = 9001; -- ok -- D) UPDATE PK in Eltern UPDATE kategorie SET kategorie_id = 5 WHERE kategorie_id = 2; -- scheitert bei referenzierten Zeilen === Case: Eishockeyverein (Team → viele Blöcke) === **FK:** //block.team_id// → //team.team_id// **Daten** INSERT INTO team (team_id, name, altersklasse) VALUES (100,'U18','U18'), (200,'Herren','Aktiv'); INSERT INTO block (block_id, bezeichnung, team_id) VALUES (301,'Erste Linie',100); **Tests A–D** -- A) INSERT INSERT INTO block (block_id, bezeichnung, team_id) VALUES (302,'Powerplay',200); -- ok INSERT INTO block (block_id, bezeichnung, team_id) VALUES (303,'Penaltykill',999); -- FK-Fehler -- B) DELETE Eltern DELETE FROM team WHERE team_id = 100; -- scheitert (RESTRICT), Block 301 hängt dran -- C) UPDATE FK im Kind UPDATE block SET team_id = 200 WHERE block_id = 301; -- ok -- D) UPDATE PK in Eltern UPDATE team SET team_id = 250 WHERE team_id = 200; -- scheitert, wenn Blocks auf 200 zeigen === Case: Öffentlicher Verkehr (Linie → viele Fahrten) === **FK:** //fahrt.linien_id// → //linie.linien_id// **Daten** INSERT INTO linie (linien_id, name, betreiber) VALUES (7,'Tram 7','VBZ'), (9,'Bus 9','VBZ'); INSERT INTO fahrt (fahrt_id, datum, abfahrtszeit, preis, linien_id) VALUES (7001,'2025-11-05','07:45',3.80,7); **Tests A–D** -- A) INSERT INSERT INTO fahrt (fahrt_id, datum, abfahrtszeit, preis, linien_id) VALUES (7002,'2025-11-05','08:15',3.80,9); -- ok INSERT INTO fahrt (fahrt_id, datum, abfahrtszeit, preis, linien_id) VALUES (7003,'2025-11-05','09:00',3.80,99); -- FK-Fehler -- B) DELETE Eltern DELETE FROM linie WHERE linien_id = 7; -- scheitert (RESTRICT) -- C) UPDATE FK im Kind UPDATE fahrt SET linien_id = 9 WHERE fahrt_id = 7001; -- ok -- D) UPDATE PK in Eltern UPDATE linie SET linien_id = 10 WHERE linien_id = 9; -- scheitert bei referenzierten Zeilen === Case: Schweizer Regionen (Kanton → viele Gemeinden) === **FK:** //gemeinde.kanton_id// → //kanton.kanton_id// **Daten** INSERT INTO kanton (kanton_id, name, einwohnerzahl, flaeche) VALUES (1,'ZH',1550000,1729), (2,'GR',200000,7105); INSERT INTO gemeinde (gemeinde_id, name, plz, kanton_id) VALUES (10001,'Zürich','8001',1); **Tests A–D** -- A) INSERT INSERT INTO gemeinde (gemeinde_id, name, plz, kanton_id) VALUES (10002,'Chur','7000',2); -- ok INSERT INTO gemeinde (gemeinde_id, name, plz, kanton_id) VALUES (10003,'Nowhere','9999',999); -- FK-Fehler -- B) DELETE Eltern DELETE FROM kanton WHERE kanton_id = 1; -- scheitert (RESTRICT), Zürich hängt dran -- C) UPDATE FK im Kind UPDATE gemeinde SET kanton_id = 2 WHERE gemeinde_id = 10001; -- ok -- D) UPDATE PK in Eltern UPDATE kanton SET kanton_id = 3 WHERE kanton_id = 2; -- scheitert bei referenzierten Zeilen ==== 4) (Optional) SET NULL oder CASCADE bewusst einsetzen ==== Falls in **eurem Modell** eine Beziehung **optional** ist (FK darf NULL sein), könnt ihr ''ON DELETE SET NULL'' wählen: ALTER TABLE ADD CONSTRAINT fk__ FOREIGN KEY () REFERENCES () ON DELETE SET NULL ON UPDATE RESTRICT; //Einsatzbeispiel:// ''team.trainer_id'' darf leer sein → beim Löschen eines Trainers bleibt das Team bestehen, ''trainer_id'' wird **NULL**. **CASCADE** verwenden wir bevorzugt bei **Zwischentabellen** (N:M). Falls euer Case eine N:M-Erweiterung hat (z. B. im Kleidershop: //Artikel ↔ Grösse//), legt ihr die Zwischen-Tabelle wie in **LU08e** an und setzt dort **CASCADE**. Für reine 1:N-Beziehungen bleibt **RESTRICT** meist die beste Wahl. ==== 5) Abgabe (pro Gruppe) ==== **Kurz-Beschrieb (Word-Dokument oder PDF, max. 1 Seite)** mit: * ERD (Crow’s Foot) mit PK/FK markiert. * Liste eurer **ALTER TABLE**-Befehle (FK-Namen, ON DELETE/UPDATE-Regeln). * 3–5 **DML-Beispiele** (INSERT/UPDATE/DELETE) inkl. kurzer Interpretation des Verhaltens (z. B. Fehlermeldung bei RESTRICT).