2016-11-01 6 views
4

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; 

Antwort

2

Es ist überraschend, aber es scheint, dass eine Kaskadierung Anweisung löscht auf productsimmer tritt auf, nachdem ein Lösch auf durchgeführt wird, customers - auch wenn keine Kunden gelöscht werden. Zum Beispiel:

SQL> delete customer where customerid = 9999999; 
delete customer where customerid = 9999999 
     * 
ERROR at line 1: 
ORA-00036: maximum number of recursive SQL levels (50) exceeded 
ORA-06512: at "TTEST.REMOVECUSTOMER", line 2 
... 

Mit Ihrer zweiten Version des Abzugs, die for Schleifenkörper nie ausgeführt werden, wenn es keine Kunden ohne Produkte sind, so dass das Löschen von customers nie auftritt und die Endlosschleife vermieden werden.

+0

Mit der zweiten Version des Triggers, selbst wenn die Schleife ausgeführt wird, gibt es keine Rekursion. Ist das normal ? – TTK

+1

@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. –

+0

@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

Verwandte Themen