Gegeben ist eine Tabelle <table> mit einer Kolumne <x>, deren
Wert frei manipuliert werden kann, und einer Kolumne <y>, deren
Wert funktional von <x> abhängt (also nicht frei manipuliert werden
kann). Die Abbildung von <x> auf <y> ist in einer Lookup-Tabelle
<lookup_table> definiert, die jedem Wert <x> den
entsprechenden Wert <y> zuordnet.
Aufgabe: Der <y>-Wert soll in <table> gespeichert werden und
stets konstitent zu <x> sein. Der Wert von <y> soll nicht
erst bei Abfrage ermittelt werden. Im Prinzip ist das eine unerwüschte
Redundanz, die aber aus praktischen Gründen sinnvoll sein kann.
Die Anforderung lässt sich durch einen BEFORE INSERT OR
UPDATE-Trigger erfüllen, der beim Einfügen oder Ändern in <table>
den <y>-Wert via <x> in <lookup_table> ermittelt und auf
<table>.<y> überträgt.
Es folgt die Lösung für Oracle und PostgreSQL.
Oracle
1 CREATE OR REPLACE TRIGGER <tigger> BEFORE INSERT OR UPDATE
2 ON <table> FOR EACH ROW
3 BEGIN
4 SELECT
5 <y>
6 INTO
7 :new.<y>
8 FROM
9 <lookup_table>
10 WHERE
11 <x> = :new.<x>;
12 END;
PostgreSQL
1 CREATE FUNCTION <trigger_func>() RETURNS trigger AS $$
2 BEGIN
3 SELECT
4 <y>
5 INTO STRICT
6 NEW.<y>
7 FROM
8 <lookup_table>
9 WHERE
10 <x> = NEW.<x>;
11
12 RETURN NEW;
13 END;
14 $$ LANGUAGE plpgsql;
15
16 CREATE TRIGGER <trigger> BEFORE INSERT OR UPDATE
17 ON <table> FOR EACH ROW
18 EXECUTE PROCEDURE <trigger_func>();
Der Code ist bei beiden Datenbanksystemen ähnlich, die Unterschiede sind im
Wesentlichen:
-
Die Prozedur kann bei Oracle als Teil des Trigger definiert werden, was
den Code einfach und elegant macht. Bei PostgreSQL ist eine
spezielle, vom Trigger getrennte Prozedurdefinition erforderlich.
-
Der Bezeichner für die neue Tabellenzeile ist bei Oracle :new,
bei PostgreSQL NEW. Bei PostgreSQL muss diese Zeile von
der Trigger-Prozedur zurückgeliefert werden. Bei Oracle wird nichts
zurückgeliefert.
-
PostgreSQL generiert bei INTO STRICT automatisch eine Exception,
wenn durch das SELECT keine oder mehr als eine Zeile getroffen wird.
Dies ist ein kostenloser Paranoia-Test für die Daten in
<lookup_table>.