20 TRIGGER

20.1 Was ist das?

Ein Trigger ist ein Programmteil, der automatisch ausgeführt wird, wenn bestimmte Datenbankoperationen (INSERT, UPDATE oder DELETE) auf einer Tabelle ausgeführt werden. Trigger können vor oder nach der Operation ausgelöst werden.

-- Grundlegende Trigger-Syntax
DELIMITER //

CREATE TRIGGER trigger_name
    {BEFORE | AFTER} {INSERT | UPDATE | DELETE}
    ON tabelle
    FOR EACH ROW
BEGIN
    -- Trigger-Code
END //

DELIMITER ;

Anwendungsfälle für Trigger:

20.2 Ein Beispiel für einen INSERT-Trigger

DELIMITER //

-- Lagerbestand bei neuer Bestellung aktualisieren
CREATE TRIGGER bestellung_nach_insert
AFTER INSERT ON bestellpositionen
FOR EACH ROW
BEGIN
    -- Lagerbestand reduzieren
    UPDATE produkte
    SET lagerbestand = lagerbestand - NEW.menge
    WHERE produkt_id = NEW.produkt_id;
    
    -- Protokollierung
    INSERT INTO log_tabelle
    (zeitpunkt, aktion, tabelle, datensatz_id)
    VALUES
    (NOW(), 'INSERT', 'bestellpositionen', NEW.position_id);
END //

-- Prüfung vor dem Einfügen
CREATE TRIGGER bestellung_vor_insert
BEFORE INSERT ON bestellpositionen
FOR EACH ROW
BEGIN
    DECLARE v_lagerbestand INT;
    
    -- Aktuellen Lagerbestand ermitteln
    SELECT lagerbestand
    INTO v_lagerbestand
    FROM produkte
    WHERE produkt_id = NEW.produkt_id;
    
    -- Prüfen ob genug auf Lager
    IF v_lagerbestand < NEW.menge THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Nicht genügend Lagerbestand!';
    END IF;
END //

DELIMITER ;

20.3 Ein Beispiel für einen UPDATE-Trigger

DELIMITER //

-- Änderungen am Kundenrabatt protokollieren
CREATE TRIGGER kunde_vor_update
BEFORE UPDATE ON kunden
FOR EACH ROW
BEGIN
    -- Wenn sich der Rabatt ändert
    IF NEW.rabatt != OLD.rabatt THEN
        -- Protokolleintrag erstellen
        INSERT INTO rabatt_historie
        (kunde_id, alter_rabatt, neuer_rabatt, aenderungsdatum)
        VALUES
        (OLD.kunde_id, OLD.rabatt, NEW.rabatt, NOW());
    END IF;
END //

-- Automatische Statusaktualisierung
CREATE TRIGGER bestellung_nach_update
AFTER UPDATE ON bestellungen
FOR EACH ROW
BEGIN
    -- Bei Änderung auf "versendet"
    IF NEW.status = 'versendet' AND OLD.status != 'versendet' THEN
        -- Versanddatum setzen
        UPDATE bestellungen
        SET versanddatum = CURRENT_DATE
        WHERE bestell_id = NEW.bestell_id;
        
        -- E-Mail-Benachrichtigung in Warteschlange
        INSERT INTO mail_queue
        (empfaenger_id, betreff, nachricht)
        SELECT 
            k.kunde_id,
            'Ihre Bestellung wurde versendet',
            CONCAT('Bestellung ', NEW.bestellnummer, ' ist unterwegs')
        FROM kunden k
        WHERE k.kunde_id = NEW.kunde_id;
    END IF;
END //

DELIMITER ;

20.4 Ein Beispiel für einen DELETE-Trigger

DELIMITER //

-- Archivierung vor dem Löschen
CREATE TRIGGER bestellung_vor_delete
BEFORE DELETE ON bestellungen
FOR EACH ROW
BEGIN
    -- Bestellung archivieren
    INSERT INTO bestellungen_archiv
    SELECT *, NOW() as archiv_datum
    FROM bestellungen
    WHERE bestell_id = OLD.bestell_id;
    
    -- Bestellpositionen archivieren
    INSERT INTO bestellpositionen_archiv
    SELECT *, NOW() as archiv_datum
    FROM bestellpositionen
    WHERE bestell_id = OLD.bestell_id;
    
    -- Bestellpositionen löschen
    DELETE FROM bestellpositionen
    WHERE bestell_id = OLD.bestell_id;
END //

-- Lagerbestand nach Stornierung aktualisieren
CREATE TRIGGER bestellposition_nach_delete
AFTER DELETE ON bestellpositionen
FOR EACH ROW
BEGIN
    -- Lagerbestand erhöhen
    UPDATE produkte
    SET lagerbestand = lagerbestand + OLD.menge
    WHERE produkt_id = OLD.produkt_id;
    
    -- Protokollierung
    INSERT INTO log_tabelle
    (zeitpunkt, aktion, tabelle, datensatz_id)
    VALUES
    (NOW(), 'DELETE', 'bestellpositionen', OLD.position_id);
END //

DELIMITER ;

Praktische Tipps:

  1. Trigger kurz halten:
-- Komplexe Logik in Stored Procedure auslagern
DELIMITER //

CREATE PROCEDURE bestellung_verarbeiten(
    IN p_bestell_id INT
)
BEGIN
    -- Komplexe Verarbeitung
END //

CREATE TRIGGER bestellung_nach_insert
AFTER INSERT ON bestellungen
FOR EACH ROW
BEGIN
    -- Nur Prozedur aufrufen
    CALL bestellung_verarbeiten(NEW.bestell_id);
END //

DELIMITER ;
  1. Endlosschleifen vermeiden:
-- Gefährlich: Gegenseitige Trigger
CREATE TRIGGER tabelle1_update
AFTER UPDATE ON tabelle1
FOR EACH ROW
    UPDATE tabelle2 ...;  -- Löst tabelle2_update aus

CREATE TRIGGER tabelle2_update
AFTER UPDATE ON tabelle2
FOR EACH ROW
    UPDATE tabelle1 ...;  -- Löst tabelle1_update aus
  1. Fehlerbehandlung einbauen:
DELIMITER //

CREATE TRIGGER sicherer_trigger
BEFORE INSERT ON wichtige_tabelle
FOR EACH ROW
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Fehler im Trigger';
    END;
    
    -- Trigger-Logik
END //

DELIMITER ;
  1. Trigger dokumentieren:
-- Kommentare für Trigger
DELIMITER //

CREATE TRIGGER bestellung_trigger
AFTER INSERT ON bestellungen
FOR EACH ROW
BEGIN
    /* Trigger: bestellung_trigger
       Zweck: Automatische Verarbeitung neuer Bestellungen
       Erstellt von: Max Mustermann
       Datum: 2024-01-15
       
       Funktionsweise:
       1. Lagerbestand aktualisieren
       2. Protokolleintrag erstellen
       3. E-Mail-Benachrichtigung senden */
    
    -- Trigger-Logik
END //

DELIMITER ;