Hallo Andreas,
bezüglich Ummodellierung geb´ ich Dir absolut recht; eine
Lösung via Trigger ist immer gefährlich - zumal mann
irgendwann vergessen hat, dass ein solchiger existiert.
Tut mir leid, aber die ist nicht gefährlich sondern falsch - zumindest solange du keinen Table Lock machst.
Es geht um das Szenario, das ich schon im ersten Post beschrieben hatte:
-
Session A fügt einen neuen Satz ein
-
Trigger wird ausgeführt --> Satz existiert noch nicht, alles ok
-
Die Transaktion von User A wird aber noch nicht abgeschlossen, d.h. der Satz ist für andere Sessions nicht sichtbar
-
Session B fügt den gleichen Satz ein
-
Trigger wird ausgeführt wobei der Satz aus Session A nicht gefunden wird und der Satz wird ebenfalls eingefügt
-
Session A macht den commit
-
Session B macht den commit
Genau jetzt hast du den Satz doppelt ohne es bemerkt zu haben. Folglich MUSS hier ein Table Lock von Session A gemacht werden, dann müsste Session B eben bis zum commit von A warten, danach findet er den Satz auch. Man hat jetzt halt ein Einzelplatzsystem gebaut, wenn das nicht stört…
Ich würde es trotzdem so machen:
CREATE TABLE tmp\_tst1 (id NUMBER(9) NOT NULL,
flag VARCHAR2(1) NOT NULL
CHECK (flag IN ('T','F')),
data VARCHAR2(1000));
CREATE TABLE tmp\_tst2 (id NUMBER(9) NOT NULL,
data VARCHAR2(1000));
CREATE UNIQUE INDEX uk\_tst1 ON tmp\_tst1(DECODE(flag,'T',id,NULL));
CREATE OR REPLACE PROCEDURE tmp\_proc1 (id IN NUMBER,
flag IN VARCHAR2,
data IN VARCHAR2) AS
BEGIN
INSERT INTO tmp\_tst1 (id, flag, data) VALUES (id, flag, data);
EXCEPTION WHEN DUP\_VAL\_ON\_INDEX THEN
INSERT INTO tmp\_tst2 (id, data) VALUES (id, data);
END tmp\_proc1;
/
EXEC tmp\_proc1(1,'T','1: first insert');
EXEC tmp\_proc1(1,'F','1: second insert');
EXEC tmp\_proc1(1,'T','1: third insert');
EXEC tmp\_proc1(2,'F','2: first insert');
EXEC tmp\_proc1(2,'T','2: second insert');
Das ist dann auch mehrplatzfähig.
Falls man keinen Zugriff auf den Programmcode hat (und daher die INSERTs nicht durch Prozeduraufrufe ersetzen kann), dann könnte man z.B. die Tabelle umbenennen, einen View erstellen, der gleich wie zuvor die Tabelle heisst und auf diesen View einen INSTEAD OF Trigger anlegen (evtl. geht das mittlerweile auch schon auf Tabellen, dann spart man sich noch den Umweg mit dem View).
Gruß
Martin