防止在 Oracle 中删除某些行

Posted

技术标签:

【中文标题】防止在 Oracle 中删除某些行【英文标题】:Preventing certain rows from being deleted in Oracle 【发布时间】:2012-02-01 19:39:43 【问题描述】:

我想防止在某个表中删除带有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 表是空的。我做错了吗?

【问题讨论】:

【参考方案1】:

如果您的触发器引发错误,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 语句的上下文之外提交。但是,在使用自主事务时要非常小心

    如果您发现自己将自治事务用于写入日志表之外的其他任何事情,那么您几乎肯定做错了什么。 使用自治事务声明的 PL/SQL 块中的代码是真正自治的,因此它看不到当前会话所做的未提交更改。 由于写入一致性,Oracle 完全有可能部分执行DELETE 语句,多次触发行级触发器,回滚该工作,然后重新执行DELETE。但是,这种静默回滚不会回滚自主事务所做的更改。因此,完全有可能单行中的单个DELETE 实际上会导致触发器被触发多次,因此会在TPM_AUDIT 中创建多行。

【讨论】:

谢谢!我将研究这种方法,尽管我现在认为审计可能是一个更好的功能,因为它也将包含 SQL 文本(我无法从触发器中获得)。基本上,我试图弄清楚为什么这些行每月会被随机删除几次,即使代码库中没有任何内容可以从该表中删除任何内容。【参考方案2】:

如果您可以在 TPM_PROJECTVERSION pk 列 + 版本列上创建唯一的 约束,那么您可以创建第二个表来引用这些行。

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

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

如果您撤销对该帮助表的 DELETE 权限,则永远无法删除这些行。

【讨论】:

【参考方案3】:

我认为您需要在调用 RAISE_APPLICATION_ERROR 之前提交 INSERT 操作,这会回滚事务。

【讨论】:

你不能在触发器中提交

以上是关于防止在 Oracle 中删除某些行的主要内容,如果未能解决你的问题,请参考以下文章

防止 Inno Setup 在卸载时删除某些文件

当某些行已经存在时如何防止SQL插入? [复制]

添加/删除 UITableView 行时如何防止文本字段变空?

SQLite:*防止* PRIMARY KEY 值在删除所有行后重置 [重复]

防止 GCC LTO 删除函数

如何防止特定组访问某些 Spring 应用程序上下文文件?