LU08c - Fremdschlüssel nachträglich hinzufügen und Referenzaktionen

Lernziele

Wozu nachträglich?

Eine Datenbank entsteht selten in einem Zug. Oft gibt es die Tabellen schon, samt Daten, und dann kommt eine neue Spalte oder eine neue Verknüpfung dazu.

Bei Ihren Cases aus LU05 ist das genau so. Dort haben Sie die Spalten für die Fremdschlüssel angelegt, aber noch nicht als Fremdschlüssel gesetzt. Das holen Sie mit ALTER TABLE nach.

Wir bleiben beim Reiseblog der letzten Seite. Zur Erinnerung: Caro hat die user_id 1, Martin die 2, Shaolin die 3.

ALTER TABLE

Zuerst kommt die neue Spalte in die bestehende Tabelle:

ALTER TABLE kind_tabelle
ADD COLUMN neue_spalte DATENTYP;

Danach wird genau diese Spalte zum Fremdschlüssel:

ALTER TABLE kind_tabelle
ADD CONSTRAINT fk_name
FOREIGN KEY (neue_spalte)
REFERENCES eltern_tabelle (primaerschluessel)
ON DELETE referenzaktion;

Den fk_name wählen Sie selbst, zum Beispiel fk_posts_editor. So erkennt man bei Fehlermeldungen, welcher Constraint sich hier bemerkbar macht.

Die drei Referenzaktionen

Mit ON DELETE legen Sie fest, was mit den Kindzeilen passiert, wenn ihre Elternzeile gelöscht wird.

Referenzaktion Was passiert mit den Kindzeilen? Beispiel
RESTRICT Nichts. Das Löschen der Elternzeile wird verhindert. Eine Autorin mit Posts kann nicht gelöscht werden
CASCADE Sie werden mitgelöscht. Wird ein Post gelöscht, verschwinden seine Kommentare
SET NULL Sie bleiben, der Verweis wird leer (NULL). Eine Redaktorin wird gelöscht, ihre Posts bleiben

Ohne Angabe gilt RESTRICT. Das haben wir auf der letzten Seite gesehen, als sich Martin nicht löschen liess.

In MySQL heisst der Standard genau genommen NO ACTION. Er verhält sich gleich wie RESTRICT.
Neben ON DELETE gibt es auch ON UPDATE, das beim Ändern eines Primärschlüssels greift. Primärschlüssel werden in der Regel nie geändert, deshalb lassen wir es weg. Es gilt dann ebenfalls RESTRICT.

Für die Wahl hilft eine Frage: Ergibt die Kindzeile ohne ihre Elternzeile noch Sinn?

Beispiel 1: SET NULL

Jeder Post bekommt eine Redaktorin, die den Text gegenliest (editor_id). Das ist freiwillig. Wird diese Person gelöscht, soll der Post bleiben. Nur der Verweis fällt weg.

Spalte ergänzen und befüllen:

ALTER TABLE posts
ADD COLUMN editor_id INT NULL;
 
UPDATE posts SET editor_id = 2 WHERE post_id = 1;  -- Hasselt: Martin liest gegen
UPDATE posts SET editor_id = 1 WHERE post_id = 2;  -- Utrecht: Caro liest gegen
UPDATE posts SET editor_id = 3 WHERE post_id = 3;  -- Lissabon: Shaolin liest gegen

Wichtig ist das NULL bei der Spalte. Ohne dieses dürfte die Spalte nie leer sein, und SET NULL könnte nicht funktionieren.

Fremdschlüssel setzen:

ALTER TABLE posts
ADD CONSTRAINT fk_posts_editor
FOREIGN KEY (editor_id)
REFERENCES users (user_id)
ON DELETE SET NULL;

Testen:

DELETE FROM users WHERE username = 'shaolin';
 
SELECT post_id, title, editor_id FROM posts ORDER BY post_id;

Shaolin ist gelöscht. Den Lissabon-Post gibt es weiterhin, seine editor_id ist jetzt NULL.

Shaolin liess sich löschen, weil er bei keinem Post Autor ist. Der Fremdschlüssel author_id blockiert also nichts. Bei Martin sieht das anders aus: Er ist Autor von drei Posts, und author_id steht weiterhin auf RESTRICT. Martin lässt sich deshalb immer noch nicht löschen.

Beispiel 2: CASCADE

Jetzt kommen Kommentare dazu. Jeder Kommentar gehört zu genau einem Post. Ohne seinen Post ergibt ein Kommentar keinen Sinn. Wird der Post gelöscht, sollen seine Kommentare deshalb automatisch mitgehen.

Tabelle anlegen:

CREATE TABLE comments (
  comment_id INT AUTO_INCREMENT PRIMARY KEY,
  post_id    INT NOT NULL,
  author     VARCHAR(100) NOT NULL,
  body       TEXT NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (post_id) REFERENCES posts (post_id)
    ON DELETE CASCADE
);

Hier steht der Fremdschlüssel direkt im CREATE TABLE, wie auf der letzten Seite. Eine Referenzaktion lässt sich auf beiden Wegen angeben.

Befüllen:

INSERT INTO comments (post_id, author, body) VALUES
  (2, 'Leser A',     'Toller Utrecht-Tipp!'),
  (2, 'Leserin B',   'Gute Café-Empfehlungen.'),
  (5, 'Reisefreund', 'Kotor war mein Highlight!');

Testen:

DELETE FROM posts WHERE post_id = 2;
 
SELECT * FROM comments;

Der Utrecht-Post ist weg, und mit ihm seine beiden Kommentare. Übrig bleibt nur der Kommentar zum Montenegro-Post.

CASCADE ist bequem, aber mit Vorsicht zu geniessen. Ein einziger DELETE kann so viele Zeilen in anderen Tabellen mitlöschen. Verwenden Sie es nur dort, wo die Kindzeilen ohne Elternzeile wirklich nutzlos sind.

Zusammenfassung

Referenzaktion Wann einsetzen?
RESTRICT Standard. Die Elternzeile soll nicht gelöscht werden, solange Kindzeilen darauf verweisen.
CASCADE Die Kindzeilen sind ohne Elternzeile nutzlos, zum Beispiel Kommentare.
SET NULL Der Verweis ist freiwillig und die Kindzeile soll bleiben.


ON DELETE: NO ACTION, SET NULL, CASCADE, SET DEFAULT (9:22 min, deutsch). Quelle: Prof. Dr. Jens Dittrich / YouTube
Das Video zeigt zusätzlich SET DEFAULT. Diese Option brauchen wir im Modul nicht.


Guido Koch