我想阻止在某个表中删除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
为空。我做错了吗?
答案 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
语句的上下文之外提交。但是要非常小心使用自治事务
DELETE
语句,多次触发行级触发器,回滚该工作,然后重新执行{{1 }}。但是,静默回滚不会回滚自治事务所做的更改。因此,单个行中的单个DELETE
实际上可能会导致触发器被多次触发,因此在DELETE
中创建多个行。答案 1 :(得分:1)
如果可以在TPM_PROJECTVERSION pk列+版本列上创建UNIQUE 约束,则可以创建第二个引用这些行的表。
尝试删除TPM_PROJECTVERSION中的行会因子行存在而失败。这至少会在您的应用程序中引发错误并阻止删除。
可以通过TPM_PROJECTVERSION上的插入触发器自动填充另一个表。
如果撤消该帮助程序表的DELETE权限,则永远不可能删除这些行。
答案 2 :(得分:0)
我认为你需要在调用RAISE_APPLICATION_ERROR之前执行INSERT操作,这会回滚事务。