触发 - 无效且重新验证失败

时间:2017-03-15 19:08:25

标签: sql oracle triggers

我正在尝试创建这个简单的触发器:

  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.

1 个答案:

答案 0 :(得分:0)

如果COMMIT块中没有使用PRAGMA AUTONOMOUS_TRANSACTION,则无法DECLARE
如果数据库允许在行级触发器中提交,则可以在评估其他行之前在一个语句中提交部分行,并将该语句作为一个单元中断 - 单个语句中的所有行都应完成其更改并且共同承诺。
如果您在此触发器中使用AUTONOMOUS_TRANSACTION,这将允许触发器在事务中执行新的UPDATEDELETE等,而不依赖于打开事务中的其他活动DML更改。 /> 但请注意,在您的情况下,您的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,因为DELETE都会有NULL :NEW.EID。如果不打算记录AFTER DELETE,或者通过DELETE声明单独处理DELETE,我建议删除CASE WHEN DELETING

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.