2017-03-15 33 views
0

我想创建这个简单的触发器:在执行触发 - 无效,未能再验证

CREATE OR REPLACE TRIGGER my_trig 
    BEFORE DELETE OR INSERT OR UPDATE ON empcopy 
    FOR EACH ROW 
    WHEN (NEW.EID > 0) 
    DECLARE 
    sal_diff number; 
    BEGIN 

sal_diff := :NEW.salary - :OLD.salary; 
dbms_output.put_line('Old salary: ' || :OLD.salary); 
dbms_output.put_line('New salary: ' || :NEW.salary); 
dbms_output.put_line('Salary difference: ' || sal_diff); 
COMMIT; 
    END; 
/

它给出结果为:

Trigger created 

但是,当我更新我的表得到以下结果:

update empcopy 
    set salary=salary+5000; 

执行之后:

error at line 1 
    ORA-0498:triiger 'HR.MY_TRIG' is invalid and failed re-validation. 
+0

貌似有一个非常类似的问题:HTTP: //stackoverflow.com/questions/22668507/oracle-trigger-ora-04098-trigger-is-invalid-and-failed-re-validation – Debangshu

+0

不适用此解决方案。 –

+0

@Jaskunwarsingh这个答案已经更新了一个完整的工作示例。请运行提供的示例,并告诉我是否收到错误。还包括你的oracle版本通常是有帮助的。 – alexgibbs

回答

0

如果不在DECLARE区块中使用PRAGMA AUTONOMOUS_TRANSACTION,则不能使用COMMIT
如果数据库允许在行级触发器中提交,则可以在其他语句被评估之前提交语句中的部分行,并将该语句作为一个单元进行中断 - 单个语句中的所有行应完成其更改并一起承诺。
如果您要在此触发器中使用AUTONOMOUS_TRANSACTION,这将允许触发器在独立于打开事务中的其他活动DML更改的事务中执行新的UPDATE s,DELETE s等。
不过需要注意,在你的情况下,你的TRIGGER实际上并不执行任何突变DML,甚至也没有任何读取,所以你并不需要一个COMMIT可言。你所需要做的就是放下你的COMMIT,如下所示。

CREATE OR REPLACE TRIGGER my_trig 
BEFORE DELETE OR INSERT OR UPDATE ON empcopy 
FOR EACH ROW 
WHEN (NEW.EID > 0) 
    DECLARE 
    sal_diff number; 
    BEGIN 

    sal_diff := :NEW.salary - :OLD.salary; 
    dbms_output.put_line('Old salary: ' || :OLD.salary); 
    dbms_output.put_line('New salary: ' || :NEW.salary); 
    dbms_output.put_line('Salary difference: ' || sal_diff); 
    END; 
/

但是我会提出一些其他的改变。
由于其他触发器也可以更改NEW值,因此可以考虑将其设置为AFTER TRIGGER,以仅记录最终状态。
此触发器也不会记录任何DELETE s,因为DELETE s将全部具有NULL:NEW.EID。如果日志记录DELETE s没有打算,或者通过CASE WHEN DELETING声明单独处理DELETE,我建议删除AFTER DELETE

CREATE OR REPLACE TRIGGER MY_TRIG 
AFTER DELETE OR INSERT OR UPDATE ON EMPCOPY 
FOR EACH ROW 
    DECLARE 
    SAL_DIFF NUMBER; 
    BEGIN 
    CASE WHEN DELETING 
     THEN 
     DBMS_OUTPUT.put_line('Log the delete here if you want.'); 
     WHEN (:NEW.EID > 0) 
     THEN 
     SAL_DIFF := COALESCE(:NEW.SALARY, 0) - COALESCE(:OLD.SALARY, 0); 
     DBMS_OUTPUT.put_line('Old salary: ' || :OLD.SALARY); 
     DBMS_OUTPUT.put_line('New salary: ' || :NEW.SALARY); 
     DBMS_OUTPUT.put_line('Salary difference: ' || SAL_DIFF); 
    ELSE NULL; 
    END CASE; 
    END; 
/

另外DBMS_OUTPUT是瞬态记录。如果您想更长期记录EMPCOPY的更改记录,Oracle提供了可用于自动化和控制数据变更跟踪的工具,例如审计追踪和FGA。

编辑:下面的例子。

创建一个测试表:

CREATE TABLE EMPCOPY(
    EID NUMBER NOT NULL, 
    SALARY NUMBER 
); 
Table EMPCOPY created. 

然后创建触发器:

CREATE OR REPLACE TRIGGER MY_TRIG 
AFTER DELETE OR INSERT OR UPDATE ON EMPCOPY 
FOR EACH ROW 
    DECLARE 
    SAL_DIFF NUMBER; 
    BEGIN 
    CASE WHEN DELETING 
     THEN 
     DBMS_OUTPUT.put_line('Log the delete here if you want.'); 
     WHEN (:NEW.EID > 0) 
     THEN 
     SAL_DIFF := COALESCE(:NEW.SALARY, 0) - COALESCE(:OLD.SALARY, 0); 
     DBMS_OUTPUT.put_line('Old salary: ' || :OLD.SALARY); 
     DBMS_OUTPUT.put_line('New salary: ' || :NEW.SALARY); 
     DBMS_OUTPUT.put_line('Salary difference: ' || SAL_DIFF); 
    ELSE NULL; 
    END CASE; 
    END; 
/
Trigger MY_TRIG compiled 

然后对其进行测试:

SQL> --Should not log, EMPID is not greater than zero. 
SQL> INSERT INTO EMPCOPY VALUES (-13, 50000); 
1 row inserted. 
SQL> --Should log, EMPID is greater than zero. 
SQL> INSERT INTO EMPCOPY VALUES (1919, 75000); 
Old salary: 
New salary: 75000 
Salary difference: 75000 
1 row inserted. 
SQL> -- The statement you provided. This should log for EMPID=1919 but not EMPID=-13 
SQL> update empcopy 
    2 set salary=salary+5000; 
Old salary: 75000 
New salary: 80000 
Salary difference: 5000 
2 rows updated. 
SQL> -- This should log a PLACEHOLDER value for each row on delete. 
SQL> DELETE FROM EMPCOPY; 
Log the delete here if you want. 
Log the delete here if you want. 
2 rows deleted. 
+0

但是当我更新empcopy它会给出一个错误。 –

+0

@Jaskunwarsingh错误是什么?你使用的是哪种版本的触发器,BEFORE还是AFTER?你能否发布你用来创建触发器的代码和引发错误的代码?这将有助于看到执行什么来评估事物。你还在用什么版本的oracle?谢谢 – alexgibbs

相关问题