Oracle 中更新或插入之前的 SQL 触发器

问题描述 投票:0回答:1

我在或插入之前创建了一个复合触发器。

有很多要求,其中我面临的一个问题是,对于 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;
/

enter image description here

列中只有一行,但有 0 或空值,因此我能够为 A_MAT = 1 插入一行,因为它不会违反 FEHLERMELDUNG for A_MAT = 1 的一行条件 如果我尝试将第一行更新为 A_MAT = 1,则触发器会触发错误,这很好。 问题是,如果我尝试使用 P_MAT = 1 更新第二行,那么我也会收到错误。

enter image description here

这是为什么呢?我的期望是,如果我更新列 P_MAT = 1 , :NEW.A_MAT 绑定变量不会保持 1 。 ORA 为 -20004 ,表示 a_mat 有一个活动行。

sql plsql triggers oracle19c
1个回答
0
投票

这是一个很长的问题,没有足够的信息来重现,但我可以回答最后一条评论:我的期望是,如果我更新列 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>
© www.soinside.com 2019 - 2024. All rights reserved.