Những gì bạn gặp phải là ngoại lệ "bảng đột biến" cổ điển. Trong trình kích hoạt ROW, Oracle không cho phép bạn chạy một truy vấn đối với bảng mà trình kích hoạt được xác định - vì vậy đó là SELECT
chống lại TABLE1 trong DELETING
một phần của trình kích hoạt gây ra sự cố này.
Có một số cách để giải quyết vấn đề này. Có lẽ tốt nhất trong tình huống này là sử dụng một trình kích hoạt phức hợp, trông giống như sau:
CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG
FOR INSERT OR DELETE ON TABLE1
COMPOUND TRIGGER
TYPE NUMBER_TABLE IS TABLE OF NUMBER;
tblTABLE2_IDS NUMBER_TABLE;
BEFORE STATEMENT IS
BEGIN
tblTABLE2_IDS := NUMBER_TABLE();
END BEFORE STATEMENT;
AFTER EACH ROW IS
BEGIN
IF INSERTING THEN
UPDATE TABLE2 t2
SET t2.TABLE2NUM = :new.NUM
WHERE t2.ID = :new.TABLE2_ID;
ELSIF DELETING THEN
tblTABLE2_IDS.EXTEND;
tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.TABLE2_ID;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
IF tblTABLE2_IDS.COUNT > 0 THEN
FOR i IN tblTABLE2_IDS.FIRST..tblTABLE2_IDS.LAST LOOP
UPDATE TABLE2 t2
SET t2.TABLE2NUM = (SELECT NUM
FROM (SELECT t1.NUM
FROM TABLE1 t1
WHERE t1.TABLE2_ID = tblTABLE2_IDS(i)
ORDER BY modification_date DESC)
WHERE ROWNUM = 1)
WHERE t2.ID = tblTABLE2_IDS(i);
END LOOP;
END IF;
END AFTER STATEMENT;
END TABLE1_NUM_TRG;
Một trình kích hoạt kết hợp cho phép mỗi thời điểm (BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
và AFTER STATEMENT
) Được xử lý. Lưu ý rằng các điểm thời gian luôn được gọi theo thứ tự đã cho. Khi một câu lệnh SQL thích hợp (tức là INSERT INTO TABLE1
hoặc DELETE FROM TABLE1
) được thực thi và trình kích hoạt này được kích hoạt, điểm thời gian đầu tiên được gọi sẽ là BEFORE STATEMENT
và mã trong BEFORE STATEMENT
trình xử lý sẽ cấp phát một bảng PL / SQL để chứa một loạt các số. Trong trường hợp này, các số được lưu trữ trong bảng PL / SQL sẽ là các giá trị TABLE2_ID từ TABLE1. (Ví dụ:một bảng PL / SQL được sử dụng thay vì một mảng vì một bảng có thể chứa nhiều giá trị khác nhau, trong khi nếu chúng ta sử dụng một mảng, chúng ta phải biết trước số lượng chúng ta cần lưu trữ. Chúng tôi không thể biết trước có bao nhiêu hàng sẽ bị ảnh hưởng bởi một câu lệnh cụ thể, vì vậy chúng tôi sử dụng bảng PL / SQL).
Khi AFTER EACH ROW
đã đạt đến điểm thời gian và chúng tôi thấy rằng câu lệnh đang được xử lý là một INSERT, trình kích hoạt chỉ tiếp tục và thực hiện CẬP NHẬT cần thiết cho TABLE2 vì điều này sẽ không gây ra sự cố. Tuy nhiên, nếu một DELETE đang được thực hiện, trình kích hoạt sẽ lưu TABLE1.TABLE2_ID vào bảng PL / SQL được cấp phát trước đó. Khi AFTER STATEMENT
Cuối cùng đã đạt đến điểm thời gian, bảng PL / SQL được phân bổ trước đó được lặp lại và đối với mỗi TABLE2_ID, bản cập nhật thích hợp sẽ được thực hiện.
Tài liệu tại đây.