Lassen Sie uns als Beispiel nehmen diese beiden Tabellen: Kunden die Kunden enthalten und Produkt enthalten die Produkte/gekauft von den Kunden verwendet.Oracle: ON DELETE CASCADE verursachen Trigger Rekursion
Jedes Produkt referenziert einen Kunden durch den Fremdschlüssel CustomerID, die dem Primärschlüssel der Tabelle Kunde entspricht (sie haben auch den gleichen Namen).
Wenn ein Kunde alle Produkte gelöscht, die diese Kunden verwiesen werden gestrichen: Product.CustomerID hat das Attribut ON DELETE CASCADE.
Jetzt sagen wir, dass ein Kunde auf der Basis mindestens ein Produkt haben sollte:
wenn ein Produkt entfernt wird, wenn es das letzte Produkt eines Kunden ist, dann muss der Kunde auch entfernt werden.
CREATE OR REPLACE TRIGGER RemoveCustomer
AFTER DELETE ON Product
BEGIN
DELETE FROM Customer
WHERE CustomerID IN (
SELECT c.CustomerID
FROM Customer c
LEFT OUTER JOIN Product p
ON p.CustomerID = c.CustomerID
GROUP BY c.CustomerID HAVING COUNT(p.CustomerID) = 0
);
END;
/
Diese Lösung scheint für mich natürlich zu sein, aber Oracle mag es nicht. Bei jedem DELETE eines Produkts ich den Fehler:
ORA-00036: maximum number of recursive SQL levels (50) exceeded
Dies auch dann, wenn die DELETE würde ein Programm nicht dazu führen, entfernt werden.
Überraschenderweise funktioniert diese Syntax nur fein:
CREATE OR REPLACE TRIGGER RemoveCustomer
AFTER DELETE ON Product
BEGIN
FOR my_row IN (
SELECT c.CustomerID
FROM Customer c
LEFT OUTER JOIN Product p
ON p.CustomerID = c.CustomerID
GROUP BY c.CustomerID HAVING COUNT(p.CustomerID) = 0
)
LOOP
DELETE FROM Customer WHERE CustomerID = my_row.CustomerID;
END LOOP;
END;
/
Könnte jemand erklären, warum dies geschieht?
EDIT:
Hier gibt es ein funktionierendes Beispiel:
CREATE TABLE Customer (
CustomerID INTEGER PRIMARY KEY
);
CREATE TABLE Product (
ProductID INTEGER PRIMARY KEY,
CustomerID INTEGER,
CONSTRAINT fk_Customer FOREIGN KEY (CustomerID)
REFERENCES Customer
ON DELETE CASCADE
);
INSERT INTo Customer (CustomerID) VALUES (0);
INSERT INTo Customer (CustomerID) VALUES (1);
INSERT INTo Customer (CustomerID) VALUES (2);
INSERT INTo Customer (CustomerID) VALUES (3);
INSERT INTo Customer (CustomerID) VALUES (4);
INSERT INTo Customer (CustomerID) VALUES (5);
INSERT INTo Customer (CustomerID) VALUES (6);
INSERT INTO Product (ProductID, CustomerID) VALUES (0, 0);
INSERT INTO Product (ProductID, CustomerID) VALUES (1, 0);
INSERT INTO Product (ProductID, CustomerID) VALUES (2, 1);
INSERT INTO Product (ProductID, CustomerID) VALUES (3, 2);
INSERT INTO Product (ProductID, CustomerID) VALUES (4, 3);
INSERT INTO Product (ProductID, CustomerID) VALUES (5, 3);
INSERT INTO Product (ProductID, CustomerID) VALUES (6, 3);
INSERT INTO Product (ProductID, CustomerID) VALUES (7, 4);
INSERT INTO Product (ProductID, CustomerID) VALUES (8, 5);
INSERT INTO Product (ProductID, CustomerID) VALUES (9, 5);
INSERT INTO Product (ProductID, CustomerID) VALUES (10, 6);
CREATE OR REPLACE TRIGGER RemoveCustomer
AFTER DELETE ON Product
BEGIN
DELETE FROM Customer
WHERE CustomerID IN (
SELECT c.CustomerID
FROM Customer c
LEFT OUTER JOIN Product p
ON p.CustomerID = c.CustomerID
GROUP BY c.CustomerID HAVING COUNT(p.CustomerID) = 0
);
END;
/
/* This request will produce the error */
DELETE FROM Product WHERE CustomerID = 3;
Mit der zweiten Version des Triggers, selbst wenn die Schleife ausgeführt wird, gibt es keine Rekursion. Ist das normal ? – TTK
@TTK - Die Schleife wird ausgeführt, findet aber beim zweiten Mal keine übereinstimmenden Zeilen, so dass das Löschen innerhalb der Schleife * nicht * erneut ausgeführt wird. –
@Alex Poole - Wenn ich richtig liege, nehmen wir an, dass nur ein Kunde entfernt werden muss: Wenn die Schleife das erste Mal ausgeführt wird, löscht sie den Kunden. Das DELETE sollte den Trigger wegen der kaskadierenden Löschanweisung erneut ausführen. An diesem Punkt haben wir eine Rekursionsebene, aber diesmal wird der Trigger nicht in die Schleife eintreten, da der einzige Kunde bereits entfernt wurde. Habe ich recht? – TTK