sql – Oracle:ON DELETE CASCADE导致触发器递归

让我们以两个表为例:包含客户的客户和包含客户购买/使用的产品的产品.

每个产品都通过外键CustomerID引用客户,该外键对应于表Customer的主键(它们也具有相同的名称).

删除客户时,将删除引用该客户的所有产品:Product.CustomerID的属性为ON DELETE CASCADE.

现在让我们说基地上的客户至少应该有一个产品:
当产品被移除时,如果它是客户的最后一个产品,那么客户也必须被移除.

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;
/

这个解决方案对我来说似乎很自然,但Oracle不喜欢它.
在每个产品的DELETE我得到错误:

ORA-00036: maximum number of recursive SQL levels (50) exceeded 

即使DELETE不会导致程序被删除.

令人惊讶的是,这种语法很好用:

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;
/

有人可以解释为什么会这样吗?

编辑:

这里有一个工作示例:

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;

最佳答案 这是令人惊讶的,但似乎在对客户执行删除后始终会对产品进行级联删除声明 – 即使没有客户被删除.例如:

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

使用第二个版本的触发器,当没有没有产品的客户时,永远不会执行for循环体,因此永远不会发生客户删除并避免无限循环.

点赞