防止在 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 中删除某些行的主要内容,如果未能解决你的问题,请参考以下文章
添加/删除 UITableView 行时如何防止文本字段变空?