我在或插入之前创建了一个复合触发器。
有很多要求,其中我面临的一个问题是,对于 5 列 A_MAT、P_MAT、P_SRC、BLUECAP 和 GRAYAWAY,FEHLERMELDUNG 一次应该只有一行有效。 有效行意味着 GUELTIG_BIS > SYSDATE 且上述 5 列中为 1。 1 可以在 1 中,也可以在所有中,这并不重要。但不可能是 2 行,其中 5 列之一有 1
这是代码
CREATE OR REPLACE TRIGGER WARNMELDUNG_TR
FOR INSERT OR UPDATE ON patrik_warnmeldung
COMPOUND TRIGGER
-- Variable to keep track of the highest sort value
highest_sort NUMBER := 0;
active_row_warnmeldung_a_mat NUMBER := 0;
active_row_fehlermeldung_a_mat NUMBER := 0;
active_row_warnmeldung_p_mat NUMBER := 0;
active_row_fehlermeldung_p_mat NUMBER := 0;
active_row_warnmeldung_p_src NUMBER := 0;
active_row_fehlermeldung_p_src NUMBER := 0;
active_row_warnmeldung_bluecap NUMBER := 0;
active_row_fehlermeldung_bluecap NUMBER := 0;
active_row_warnmeldung_grayway NUMBER := 0;
active_row_fehlermeldung_grayaway NUMBER := 0;
BEFORE STATEMENT IS
BEGIN
-- Initialize the highest sort value by querying the table
SELECT NVL(MAX(SORT), 0) INTO highest_sort FROM kbs2.patrik_warnmeldung;
-- Initialize the highest sort value by querying the table
SELECT count(*) INTO active_row_warnmeldung_a_mat FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'WARNMELDUNG' AND A_MAT = 1;
SELECT count(*) INTO active_row_fehlermeldung_a_mat FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'FEHLERMELDUNG' AND A_MAT = 1;
SELECT count(*) INTO active_row_warnmeldung_p_mat FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'WARNMELDUNG' AND P_MAT = 1;
SELECT count(*) INTO active_row_fehlermeldung_p_mat FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'FEHLERMELDUNG' AND P_MAT = 1;
SELECT count(*) INTO active_row_warnmeldung_p_src FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'WARNMELDUNG' AND P_SRC = 1;
SELECT count(*) INTO active_row_fehlermeldung_p_src FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'FEHLERMELDUNG' AND P_SRC = 1;
SELECT count(*) INTO active_row_warnmeldung_bluecap FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'WARNMELDUNG' AND BLUECAP = 1;
SELECT count(*) INTO active_row_fehlermeldung_bluecap FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'FEHLERMELDUNG' AND BLUECAP = 1;
SELECT count(*) INTO active_row_warnmeldung_grayway FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'WARNMELDUNG' AND GRAYAWAY = 1;
SELECT count(*) INTO active_row_fehlermeldung_grayaway FROM kbs2.patrik_warnmeldung WHERE GUELTIG_BIS > SYSDATE AND FEHLERTYP = 'FEHLERMELDUNG' AND GRAYAWAY = 1;
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
-- Check if the user is provided; if not, raise an exception
IF :NEW.user IS NULL THEN
RAISE_APPLICATION_ERROR(-20001, 'Insert your user id');
END IF;
-- Check if active_till is before activ_since; if so, raise an exception
IF :NEW.GUELTIG_VON IS NOT NULL AND :NEW.GUELTIG_BIS IS NOT NULL AND :NEW.GUELTIG_VON > :NEW.GUELTIG_BIS THEN
RAISE_APPLICATION_ERROR(-20002, 'active_since cannot be after active_till');
END IF;
-- Set default values for activ_since and active_till if not provided
IF :NEW.GUELTIG_VON IS NULL THEN
:NEW.GUELTIG_VON := SYSDATE;
END IF;
IF :NEW.GUELTIG_BIS IS NULL THEN
:NEW.GUELTIG_BIS := TO_DATE('31-12-9999', 'DD-MM-YYYY');
END IF;
-- Check for active row with active_till > SYSDATE, type 'WARNMELDUNG' or 'FEHLERMELDUNG', and a_mat = 1
IF :NEW.A_MAT = 1 AND :NEW.FEHLERTYP = 'WARNMELDUNG' AND active_row_warnmeldung_a_mat <> 0 THEN
RAISE_APPLICATION_ERROR(-20003, 'There is already an active row for a_mat and WARNMELDUNG, please either set active_till as today-1 or a_mat as 0');
END IF;
IF :NEW.A_MAT = 1 AND :NEW.FEHLERTYP = 'FEHLERMELDUNG' AND active_row_fehlermeldung_a_mat <> 0 THEN
RAISE_APPLICATION_ERROR(-20004, 'There is already an active row for a_mat and FEHLERMELDUNG, please either set active_till as today-1 or a_mat as 0');
END IF;
-- Check for active row with active_till > SYSDATE, type 'WARNMELDUNG' or 'FEHLERMELDUNG', and p_mat = 1
IF :NEW.P_MAT = 1 AND :NEW.FEHLERTYP = 'WARNMELDUNG' AND active_row_warnmeldung_p_mat <> 0 THEN
RAISE_APPLICATION_ERROR(-20005, 'There is already an active row for p_mat and WARNMELDUNG, please either set active_till as today-1 or p_mat as 0');
END IF;
IF :NEW.P_MAT = 1 AND :NEW.FEHLERTYP = 'FEHLERMELDUNG' AND active_row_fehlermeldung_p_mat <> 0 THEN
RAISE_APPLICATION_ERROR(-20006, 'There is already an active row for p_mat and FEHLERMELDUNG, please either set active_till as today-1 or p_mat as 0');
END IF;
-- Set sort value to the highest sort value + 1 if not provided
IF :NEW.SORT IS NULL THEN
highest_sort := highest_sort + 1;
:NEW.SORT := highest_sort;
END IF;
END BEFORE EACH ROW;
END WARNMELDUNG_TR;
/
列中只有一行,但有 0 或空值,因此我能够为 A_MAT = 1 插入一行,因为它不会违反 FEHLERMELDUNG for A_MAT = 1 的一行条件 如果我尝试将第一行更新为 A_MAT = 1,则触发器会触发错误,这很好。 问题是,如果我尝试使用 P_MAT = 1 更新第二行,那么我也会收到错误。
这是为什么呢?我的期望是,如果我更新列 P_MAT = 1 , :NEW.A_MAT 绑定变量不会保持 1 。 ORA 为 -20004 ,表示 a_mat 有一个活动行。
这是一个很长的问题,没有足够的信息来重现,但我可以回答最后一条评论:我的期望是,如果我更新列 P_MAT = 1 , :NEW.A_MAT 绑定变量不会保持 1 。这个假设是不正确的。当仅更新单个列时,触发器中的所有其他列 :OLD 和 :NEW 值将保存实际列值或当前行,而不是 NULL。检查下面的测试来证实这一点:
19c>set serveroutput on size 999999
19c>CREATE TABLE my_table
2 ( c1 varchar2(10),
3 c2 varchar2(10),
4 c3 varchar2(10)
5* );
Table MY_TABLE created.
19c>CREATE OR REPLACE TRIGGER my_table_biu
2 BEFORE INSERT OR update
3 ON my_table
4 FOR EACH ROW
5 DECLARE
6 BEGIN
7 dbms_output.put_line(':NEW.c1 = '||:NEW.c1||',:OLD.c1 = '||:OLD.c1);
8 END;
9* /
Trigger MY_TABLE_BIU compiled
19c>insert into my_table(c1,c2,c3) values ('A','B','C');
:NEW.c1 = A,:OLD.c1 =
1 row inserted.
19c>update my_table set c2 = 'B';
:NEW.c1 = A,:OLD.c1 = A
1 row updated.
19c>