2016-11-01 7 views
4

의 원인.오라클 : <strong>고객</strong>을 고객과 제품 구입/고객이 사용 포함 <strong>제품</strong>을 포함 : DELETE CASCADE ON의 예로서이 두 테이블을 보자 트리거 재귀

모든 제품은 고객 테이블의 기본 키에 해당하는 외래 키 고객 ID을 통해 고객을 참조합니다 (이름도 같습니다). 고객이 그 의상을 참조하는 모든 제품을 삭제

삭제 : Product.CustomerID는 DELETE CASCADE ON 속성 있습니다.

이제 제품을 제거 할 때 기본 고객이 적어도 제품을 가져야한다고 가정 해 봅시다. 고객이 비용을내는 최종 제품 인 경우 costumer도 함께 제거해야합니다.

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

이 솔루션은 자연스러운 것 같지만 오라클은 싫어합니다. 은 모든 나는 오류가 제품의 삭제 : DELETE 프로그램을 일으킬하지 않을 경우에도

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

이 제거 할 수 있습니다.

놀랍게도,이 구문은 잘 작동합니다 :

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; 

답변

2

그것은 놀라운 일이지만, 삭제가 수행 된 후 발생하는 계단식 항상 products에 문을 삭제하는 것이 나타납니다 customers - 삭제 된 고객이없는 경우에도 마찬가지입니다. 예를 들어 어떤 고객이 어떤 제품이없는 경우 트리거의 두 번째 버전으로

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 루프 본문이 실행되지 않습니다, 그래서는 customers가 결코 발생하지 무한 루프가 피할 수의 삭제합니다.

+0

트리거의 두 번째 버전에서는 루프가 실행될 때에도 재귀가 발생하지 않습니다. 그게 정상인가요? – TTK

+1

@TTK - 루프가 실행되지만 두 번째로 일치하는 행이 없으므로 루프 내부의 삭제가 다시 실행되지 않습니다. –

+0

@Alex Poole - 맞다면 한 고객 만 제거해야한다고 가정 해보십시오. 처음으로 루프가 실행될 때 고객에게 DELETE가 수행됩니다. 계단식 삭제 문 때문에 DELETE가 트리거를 다시 실행해야합니다. 이 시점에서 우리는 하나의 재귀 레벨을 가지지 만 이번에는 유일한 고객이 이미 제거되었으므로 트리거가 루프 내부에 들어 가지 않습니다. 내가 맞습니까? – TTK