Trigger Andreas Schmidt Trigger 125
Trigger Konzept, um automatisch auf vordefinierte Ereignisse in der Datenbank zu reagieren Trigger sind mit Datenbanktabelle verknüpft Auslösung durch DML Befehle INSERT, UPDATE und DELETE Einsatzbereiche: Datenintegrität Datensicherheit referentielle Integrität (besser durch Constraints) Produktivitätssteigerung Ausführungszeitpunkte: Vor der Ausführung des DML Befehls Nach der Ausführung des DML Befehls Anstelle eines DML-Befehls Bearbeitung durch Trigger bezieht sich entweder auf das DML-Statement (Statement Trigger) auf jeden einzelnen betroffenen Datensatz (Row Trigger) Andreas Schmidt Trigger 225
Trigger Syntax: CREATE [OR REPLACE] TRIGGER <Name> { BEFORE AFTER INSTEAD OF } { INSERT DELETE UPDATE } [OF <Spaltenliste> ]} ON <Tabellenname> [FOR EACH ROW] [ WHEN (<Prädikat>)] <Triggerrumpf> Erläuterungen INSTEAD OF Trigger bei Views 1 Vordefinierte Variablen :new und :old für den Zugriff auf die altenneuen Werte (nur bei ROW-Trigger) WHEN darf keine SQL-Anfragen enthalten keine zyklischen Abhängigkeiten erlaubt AktivierenDeaktivieren eines Triggers: ALTER TRIGGER <TRIGGERNAME> ENABLE DISABLE; Tabelle user_triggers enthält Triggercode 1. funktioniert nicht bei Tabellen Andreas Schmidt Trigger 325
Trigger Beispiele (ROW) create or replace trigger example_row_trigger after update of population on my_city for each row dbms_output.put_line('bevölkerungszuwachs in ' :old.name ' von ' :old.population ' auf ' :new.population); SQL> set serveroutput on SQL> update my_city set population=round(population*1.05) where country='d'; Bevölkerungszuwachs in Bremen von 549182 auf 576641 Bevölkerungszuwachs in Stuttgart von 588482 auf 617906 Bevölkerungszuwachs in Potsdam von 138268 auf 145181 Bevölkerungszuwachs in Wiesbaden von 266081 auf 279385 Bevölkerungszuwachs in Schwerin von 118291 auf 124206... Andreas Schmidt Trigger 425
Trigger Beispiele (STATEMENT) create or replace trigger example_statement_trigger after delete on my_city dbms_output.put_line('user ' user() ' hat ein DELETE Statement auf Tabelle my_city durchgeführt'); show errors SQL> delete my_city where population < 100000; User SCAN0004 hat ein DELETE Statement auf Tabelle my_city durchgeführt 184 Zeilen wurden gelöscht. SQL> Andreas Schmidt Trigger 525
Trigger Beispiele (Check) create or replace trigger check_establishment_time before insert on my_city if to_char(sysdate,'hh24:mi') between '00:00' and '12:00' then raise_application_error(-20001,'städte dürfen nur nachmittags gegründet werden'); end if; show errors SQL> insert into my_city (name, country, population) values('smifftown', 'D', 142252352); insert into my_city (name, country, population) * FEHLER in Zeile 1: ORA-20001: Städte dürfen nur nachmittags gegründet werden ORA-06512: in "SMIFF.CHECK_ESTABLISHMENT_TIME", Zeile 3 ORA-04088: Fehler bei der Ausf³hrung von Trigger 'SMIFF.CHECK_ESTABLISHMENT_TIME' Andreas Schmidt Trigger 625
Abarbeitungsreihenfolge create table trigger_test_table ( id number primary key, text varchar2(10) ); create or replace TRIGGER before_update before update on trigger_test_table dbms_output.put_line('before_update_stmt'); create or replace TRIGGER after_update after update on trigger_test_table dbms_output.put_line('after_update_stmt'); create or replace TRIGGER before_update_row before update on trigger_test_table for each row dbms_output.put_line('before_update_row (' :old.text '->' :new.text ')'); create or replace TRIGGER after_update_row after update on trigger_test_table for each row dbms_output.put_line('after_update_row (' :old.text '->' :new.text ')'); Andreas Schmidt Trigger 725
Abarbeitungsreihenfolge insert into trigger_test_table values(1, 'eins'); insert into trigger_test_table values(2, 'zwei'); insert into trigger_test_table values(3, 'drei'); set serveroutput on update trigger_test_table set text=upper(text) where id < 3; before_update_stmt before_update_row (eins -> EINS) after_update_row (eins -> EINS) before_update_row (zwei -> ZWEI) after_update_row (zwei -> ZWEI) after_update_stmt select * from trigger_test_table; ID TEXT ---------- ---------- 1 EINS 2 ZWEI 3 drei SQL> upper(...) wandelt eine Zeichenkette in Großbuchstaben um 2 Zeilen wurden aktualisiert. Andreas Schmidt Trigger 825
Unterscheidung der auslösenden Triggeraktion create or replace TRIGGER before_update before insert or update or delete on trigger_test_table if (INSERTING) then dbms_output.put_line('insert operation'); ELSIF (UPDATING) then dbms_output.put_line('update operation'); ELSIF (DELETING) then dbms_output.put_line('delete operation'); ELSE raise_application_error(-20001, 'Das darf eigentlich nicht passieren!'); END IF; show errors Andreas Schmidt Trigger 925
Trigger - Einsatzgebiete ROW Trigger BEFORE INSERTUPDATE: Um Spaltenwerte zu setzen oder zu transformieren erweiterte Konsistenzchecks; eventuell verwerfen des Statements mit raise_application_error(error_id, 'Text'); AFTER INSERTUPDATE: um weitere Aktionen anzuschucken BEFOREAFTER DELETE zusätzliche Aktionen vor oder nach dem Löschen STATEMENT Trigger Auditing Checks Andreas Schmidt Trigger 1025
Instead Of Trigger (1) -- Szenario: Temperaturtabelle (in Fahrenheit) mit Zeitstempel und Ort der Messung -- View der die Temperatur in der Küche in Celsius ausgibt -- alter session set NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'; create table temperature ( timestamp date, location varchar2(20), fahrenheit number(5,1), primary key (timestamp, location) ); create or replace view kitchen_temperature (timestamp, celcius) as select timestamp, (fahrenheit-32) * 5.09.0 from temperature where location='kitchen'; Andreas Schmidt Trigger 1125
Instead Of Trigger (2) create or replace trigger add_temperature_value instead of insert on kitchen_temperature for each row insert into temperature values(:new.timestamp, 'kitchen', :new.celcius * 9.05.0 + 32); show errors insert into temperature values(sysdate-224, 'kitchen', 67); insert into kitchen_temperature values(sysdate-124, 25.1); aktiviert add_temperature_value trigger -- Übung: Trigger für Update auf kitchen_temperature Andreas Schmidt Trigger 1225
Mutating Table Problem Eine Tabelle wird als Mutating Table bezeichnet, wenn Änderungen an dieser Tabelle den Trigger aktiviert haben oder diese Tabelle aufgrund von ON DELETE... Constraints eventuell modifiziert werden muss. Auf diese Tabellen darf von einem Row-Trigger aus nicht zugegriffen 1 werden. Workarounds: auf diese Tabellen nicht zugreifen ;-) Temporäre Daten im ROW-Trigger ablegen und diese in einem AFTER-Statement-Trigger verarbeiten Trigger auf View definieren und mit Instead of -Trigger arbeiten 1. sowohl lesend als auch schreibend Andreas Schmidt Trigger 1325
Übung 1: Wenn ein Posten einer Bestellung gelöscht wird, dann soll eine Neunumerierung der Positionen stattfinden (Szenario Tutorium 1, Übung 1) 1.Ansatz create or replace TRIGGER positionen_neu_berechnen0 after delete on posten for each row update posten set position=position-1 where bestellung_fk = :old.bestellung_fk and position > :old.position; Resultat (beim Löschen eines Postens): ORA-04091: Tabelle SCAN0004.POSTEN wird gerade geõndert, TriggerFunktion sieht dies m glicherweise nicht ORA-06512: in "SCAN0004.POSITIONEN_NEU_BERECHNEN0", Zeile 3 ORA-04088: Fehler bei der Ausf³hrung von Trigger 'SCAN0004.POSITIONEN_NEU_BERECHNEN0' Andreas Schmidt Trigger 1425
Lösung 1: Aufspalten in einen ROW- und einen Statement Trigger. create table temp ( bestellung number references bestellung(id), geloeschte_position number ); create or replace TRIGGER positionen_neu_berechnen1 after delete on posten for each row insert into temp values(:old.bestellung_fk, :old.position); Da man im Row Trigger nicht auf Datensätze der Tabelle Posten zugreifen kann machen wir es einfach nicht, sondern verschieben das auf den anschließenden Statement Trigger (da ist es erlaubt) Andreas Schmidt Trigger 1525
create or replace TRIGGER positionen_neu_berechnen2 after delete on posten declare cursor cur_position is select * from temp order by geloeschte_position desc; for data in cur_position loop update posten set position=position-1 where bestellung_fk = data.bestellung and position > data.geloeschte_position; end loop; Auslesen der im Row-Trigger abgespeicherten Datensätze und durchführen der Update Operation auf Tabelle Posten. delete from temp; Lösung 2: Mittels Instead of Trigger und View: siehe Beispiele zur Vorlesung Anschließend löschen der Datensätze aus Tabelle temp. Andreas Schmidt Trigger 1625
weitere Trigger Data Definition Language Event Trigger: before after create before after grant before after alter before after drop... Andreas Schmidt Trigger 1725
Triggerbeispiel create table person ( p_id number(8) primary key, vorname varchar2(60) not null, nachname varchar2(60) not null, geburtsdatum date not null, todesdatum date ); create table ist_verheiratet_mit ( ehemann_fk number(8) references person(p_id) on delete cascade, ehefrau_fk number(8) references person(p_id) on delete cascade, hochzeitsdatum date not null, trennungsdatum date, primary key(ehemann_fk, ehefrau_fk, hochzeitsdatum) ); Andreas Schmidt Trigger 1825
Konsistenzchecks Heiraten darf man nur, wenn man mindestens 18 Jahre alt ist (Mindestalter) man nicht bereits verheiratet ist (keine Polygamie)... Andreas Schmidt Trigger 1925
1. Check: Mindestalter create or replace TRIGGER bitte_nicht_mit_kindern before insert on ist_verheiratet_mit for each row declare cursor hochzeitsalter(p_hochzeitsdatum in date, er in number, sie in number) is select (p_hochzeitsdatum - p.geburtsdatum)365 hochzeitsalter from person p where p.p_id in (er, sie); for data in hochzeitsalter(:new.hochzeitsdatum, :new.ehemann_fk, :new.ehefrau_fk) loop if data.hochzeitsalter < 18 then raise_application_error(-20002,'mindestens einer der Lümmel ist zu jung um zu heiraten'); end if; end loop; Andreas Schmidt Trigger 2025
2. Check: keine Polygamie create or replace TRIGGER bitte_keine_polygamie before insert on ist_verheiratet_mit for each row declare aktuelle_beziehungen number(1); select count(*) into aktuelle_beziehungen from ist_verheiratet_mit where (ehemann_fk=:new.ehemann_fk or ehefrau_fk=:new.ehefrau_fk) and (hochzeitsdatum < :new.hochzeitsdatum and (trennungsdatum > :new.hochzeitsdatum or trennungsdatum is null)); if aktuelle_beziehungen > 0 then raise_application_error(-20001,'mindestens einer der Lümmel ist am ' :new.hochzeitsdatum ' bereits verheiratet'); end if; Andreas Schmidt Trigger 2125
Ablauf insert into person (p_id, vorname, nachname, geburtsdatum) values (1, 'Peter', 'Schulz', '20.10.1939'); insert into person (p_id, vorname, nachname, geburtsdatum) values (2, 'Heide', 'Lauer', '26.9.1940'); insert into person (p_id, vorname, nachname, geburtsdatum) values (3, 'Monika', 'Krieger', '2.2.1941'); insert into ist_verheiratet_mit (ehemann_fk, ehefrau_fk, hochzeitsdatum) values (1,2,'11.10.1954'); insert into ist_verheiratet_mit (ehemann_fk, ehefrau_fk, hochzeitsdatum) values (1,2,'11.10.1964'); update ist_verheiratet_mit set trennungsdatum='28.2.1970' where ehemann_fk=1 and ehefrau_fk=2 and hochzeitsdatum='11.10.1964'; insert into ist_verheiratet_mit (ehemann_fk, ehefrau_fk, hochzeitsdatum) values (1,3,'1.7.1968'); Andreas Schmidt Trigger 2225
Ablauf -- Eingabe der 3 Personen wie vorherige Seite SQL> insert into ist_verheiratet_mit (ehemann_fk, ehefrau_fk, hochzeitsdatum) 2 values (1,2,'11.10.1954'); insert into ist_verheiratet_mit (ehemann_fk, ehefrau_fk, hochzeitsdatum) * FEHLER in Zeile 1: ORA-20002: Mindestens einer der Lümmel ist zu jung um zu heiraten ORA-06512: in "SCAN0004.BITTE_NICHT_MIT_KINDERN", Zeile 15 ORA-04088: Fehler bei der Ausführung von Trigger 'SCAN0004.BITTE_NICHT_MIT_KINDERN' -- weitere statements wie auf vorheriger Folie... SQL> insert into ist_verheiratet_mit (ehemann_fk, ehefrau_fk, hochzeitsdatum) 2 values (1,3,'1.7.1968'); insert into ist_verheiratet_mit (ehemann_fk, ehefrau_fk, hochzeitsdatum) * FEHLER in Zeile 1: ORA-20001: Mindestens einer der Lümmel ist am 01.07.68 bereits verheiratet ORA-06512: in "SCAN0004.BITTE_KEINE_POLYGAMIE", Zeile 13 ORA-04088: Fehler bei der Ausführung von Trigger 'SCAN0004.BITTE_KEINE_POLYGAMIE' Andreas Schmidt Trigger 2325
zum Üben... Mögliche Erweiterungen: Ausgabe, wer der Beteiligten die Regel verletzt der Partner sollte am Hochzeitstag noch leben... Zusätzliches Attribut ehestand, der durch Eheschließung, Scheidung, Tod des Partners automatisch aktualisiert wird. Andreas Schmidt Trigger 2425
Literatur Constraints and Triggers http:www-db.stanford.edu~ullmanfcdboracleor-triggers.html Mutating Table Error on Cascading Delete http:www.akadia.comservicesora_mutating_table_error.html Andreas Schmidt Trigger 2525