【问题标题】:oracle Before trigger on DML operations on same tableoracle 对同一张表上的 DML 操作触发之前
【发布时间】:2017-04-03 13:53:40
【问题描述】:

要求是,

  1. 如果 DELETING,在 USER_INFO 表中创建的任何记录都不能被删除。尝试从此表中删除记录被捕获,在 AUDIT 表中创建一个条目。
  2. 如果正在插入,必填字段为 ID、LOGIN_USER

    • 在插入表之前,验证不存在具有相同 LOGIN_USER 的现有记录并且记录状态为 True。

    • 如果此验证失败,则返回错误代码和指示失败的消息。

    • 当向表中插入记录时,事件后触发器将触发更新以下字段:ID 和序列、创建当前日期/时间和操作“记录创建”

  3. 如果更新,必填字段为 ID,LOGIN_USER

    • 更新表中的记录时,将触发后事件触发器,执行以下操作:

    o 将记录插入到受影响的表中,复制字段的当前值。

    o 使用以下值填充或覆盖以下字段: 主动错误 事件“数据维护审计”

    • 事件前触发器将触发并阻止更新非活动记录。

    • 状态为 Active = False 的记录可能不会被更新。如果是这样,则必须创建一条新记录。

我已经编写了代码,它执行删除和插入没有任何问题。

问题在于 UPDATE,当它碰到 UPDATING 块时,在 else 块中插入;这里 INSERT 查询由 INSERTING 块递归触发。当我只想运行 UPDATING 块时不应该执行。

请建议,有没有其他方法可以遵循。这是我第一次使用触发代码。

CREATE OR REPLACE TRIGGER BEFORE_DML                  
BEFORE DELETE OR INSERT OR UPDATE OF ID,LOGIN_USER ON USER_INFO 
REFERENCING NEW AS NEW OLD AS OLD              
FOR EACH ROW

DECLARE
   PRAGMA AUTONOMOUS_TRANSACTION;                        
    V_USER          VARCHAR2 (30);          
    V_NODE          VARCHAR2 (50);        
    V_CNT           NUMBER   := 0;       
    V_ACTIVE        VARCHAR2 (10);      

BEGIN        
    SELECT SYS_CONTEXT ('USERENV', 'IP_ADDRESS')        
    INTO V_NODE       
    FROM DUAL;       

    IF  DELETING THEN     
        INSERT        
        INTO AUDIT (IDS,       
                    ACTION,         
                    IP_ADDRESS,         
                    LOGIN,           
                    CREATED        
                    )           
             VALUES(AUDIT_SEQ.NEXTVAL,           
                    'Deletion',        
                    V_NODE,       
                    SYS.LOGIN_USER,         
                    SYSDATE      
                    );         
        COMMIT;    
        RAISE_APPLICATION_ERROR (-20002, 'USER_INFO table data can not be deleted');    
    END IF; --Close IF for DELETING     

    IF INSERTING THEN              

        SELECT COUNT (1)          
        INTO  V_CNT        
        FROM  USER_INFO      
        WHERE LOGIN_USER = :NEW.LOGIN_USER         
        AND   STATUS='TRUE';        

            IF ( V_CNT >=1 )  THEN      
                RAISE_APPLICATION_ERROR (-20002, 'DATABASE LOGIN USER ALREADY EXISTS', TRUE);      
            END IF;     
               :NEW.ID        :=   DBLOGIN_SEQ.NEXTVAL;            
               :NEW.CREATED    :=   SYSDATE;         
               :NEW.ACTION     :=   'Record Creation' ;                
    END IF; --CLOSE IF FOR INSERTING        

    IF UPDATING THEN      

        IF  :OLD.STATUS = 'INACTIVE'          
        THEN    
            RAISE_APPLICATION_ERROR (-20002, 'INACTIVE RECORDS CAN NOT BE  UPDATED');   
        ELSE IF  :OLD.STATUS = 'FALSE'  THEN       
               INSERT INTO USER_INFO(ID,     
                                    STATUS,          
                                    LOGIN_USER,         
                                    CREATED,        
                                    ACTION        
                                    )         
                             VALUES(DBLOGIN_SEQ.NEXTVAL,      
                                    'INVALID',            
                                    :OLD.LOGIN_USER,            
                                    SYSDATE,           
                                    'FALSE RECORD UPDATED '         
                                    );          
                COMMIT;          
        ELSE      
            INSERT INTO USER_INFO(ID,           
                                  STATUS,          
                                  LOGIN_USER,            
                                  CREATED,            
                                  ACTION          
                                  )       
                           VALUES(DBLOGIN_SEQ.NEXTVAL,              
                                  'FALSE',         
                                  :OLD.LOGIN_USER, --NEW.LOGIN_USER         
                                  SYSDATE,      
                                  'Audit for data maintenance'     
                                   );
            COMMIT;    
        END IF;                                               
    END IF;       
    END IF; --CLOSE IF FOR UPDATING              

END BEFORE_DML;
/

【问题讨论】:

  • 这是家庭作业,还是您正在尝试解决实际问题?你可以做一些hack-y,你有一个带有公共全局in_before_dml_trigger变量的包,你在update块中设置并在触发器顶部检查。这将解决眼前的问题,但从维护和支持的角度来看,这是一种皇家痛苦。您在触发器中所做的事情也只能在单用户系统中工作——唯一索引实际上可以确保在多用户系统中login_user 只有一行。
  • 谢谢。这种做法正确吗?

标签: oracle plsql triggers


【解决方案1】:

一种方法是在插入之前禁用触发器,然后再次启用它。 示例:

execute immediate 'alter trigger [trigger_name] disable'

【讨论】:

  • 我很确定你不能在触发器中运行 DDL。
  • 当然可以.. 使用动态 sql
  • 这与语法无关,而与 DDL 的作用有关。 DDL 隐式执行触发器中不允许的提交
  • 好的,现在是这样。忘了说你需要用 pragma 自治事务实现一个过程,然后在触发器上调用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-29
  • 2016-06-17
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 2015-07-15
相关资源
最近更新 更多