防止在Oracle中删除某些行

时间:2012-01-03 19:50:13

标签: sql oracle triggers oracle11g

我想阻止在某个表中删除VERSIONID=1的任何行。我还想在审计表中记录它,以便我们可以看到这种情况何时发生以便进行日志记录。我试图用触发器来做这件事:

CREATE TRIGGER TPMDBO.PreventVersionDelete
  BEFORE DELETE ON TPM_PROJECTVERSION
  FOR EACH ROW
DECLARE
BEGIN
  IF( :old.VERSIONID = 1 )
  THEN
    INSERT INTO TPM_AUDIT VALUES ('Query has attempted to delete root project version!', sysdate);
    RAISE_APPLICATION_ERROR( -20001, 'Query has attempted to delete root project version!' );
  END IF;
END;

我得到以下结果:

SQL> delete from TPM_PROJECTVERSION where PROJECTID=70 and VERSIONID=1;
delete from TPM_PROJECTVERSION where PROJECTID=70 and VERSIONID=1
            *
ERROR at line 1:
ORA-20001: Query has attempted to delete root project version!
ORA-06512: at "TPMDBO.PREVENTVERSIONDELETE", line 6
ORA-04088: error during execution of trigger 'TPMDBO.PREVENTVERSIONDELETE'

但是,表TPM_AUDIT为空。我做错了吗?

3 个答案:

答案 0 :(得分:10)

如果您的触发器引发错误,则DELETE语句将失败,并且事务将回滚到在语句运行之前创建的隐式保存点。这意味着触发器所做的任何更改也会回滚。

您可以使用自治事务解决此问题。像

这样的东西
CREATE PROCEDURE write_audit
AS
  PRAGMA AUTOMOMOUS_TRANSACTION;
BEGIN
  INSERT INTO tpm_audit
    VALUES( 'Query has attempted to delete root project version!',
            sysdate );
  commit;
END;

CREATE TRIGGER TPMDBO.PreventVersionDelete
  BEFORE DELETE ON TPM_PROJECTVERSION
  FOR EACH ROW
DECLARE
BEGIN
  IF( :old.VERSIONID = 1 )
  THEN
    write_audit;
    RAISE_APPLICATION_ERROR( -20001, 'Query has attempted to delete root project version!' );
  END IF;
END;

这会将INSERT放入TPM_AUDIT到一个单独的事务中,该事务可以在DELETE语句的上下文之外提交。但是要非常小心使用自治事务

  1. 如果您发现自己使用自动事务而不是写入日志表,那么您几乎肯定会做错事。
  2. 使用自治事务声明的PL / SQL块中的代码是真正自治的,因此无法查看当前会话所做的未提交的更改。
  3. 由于写入一致性,Oracle完全有可能部分执行DELETE语句,多次触发行级触发器,回滚该工作,然后重新执行{{1 }}。但是,静默回滚不会回滚自治事务所做的更改。因此,单个行中的单个DELETE实际上可能会导致触发器被多次触发,因此在DELETE中创建多个行。

答案 1 :(得分:1)

如果可以在TPM_PROJECTVERSION pk列+版本列上创建UNIQUE 约束,则可以创建第二个引用这些行的表。

尝试删除TPM_PROJECTVERSION中的行会因子行存在而失败。这至少会在您的应用程序中引发错误并阻止删除。

可以通过TPM_PROJECTVERSION上的插入触发器自动填充另一个表。

如果撤消该帮助程序表的DELETE权限,则永远不可能删除这些行。

答案 2 :(得分:0)

我认为你需要在调用RAISE_APPLICATION_ERROR之前执行INSERT操作,这会回滚事务。