2012-01-03 140 views
5

我想阻止任何具有VERSIONID=1的行在某個表中被刪除。我也想將它記錄在審計表中,以便我們可以看到這是爲了記錄目的而發生的。我試圖用一個觸發器來做到這一點:防止在Oracle中刪除某些行

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是空的。難道我做錯了什麼?

回答

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; 

這東西會把INSERTTPM_AUDIT到一個單獨的事務可以在DELETE語句的上下文之外被提交。要非常小心使用自治事務,但

  1. 如果你發現自己使用自治事務比寫入日誌表以外的任何,你幾乎可以肯定做錯了什麼。
  2. 使用自治事務聲明的PL/SQL塊中的代碼是真正自治的,因此它看不到當前會話所做的未提交更改。
  3. 由於寫入一致性,Oracle完全有可能會部分執行一條DELETE語句,多次觸發行級觸發器,回滾該工作,然後重新執行DELETE。但是,該靜默回滾將不會回滾自治事務所做的更改。因此,一行中的單個DELETE完全可能會導致觸發器被觸發多次,因此在TPM_AUDIT中創建多行。
+0

謝謝!我將研究這種方法,但我現在認爲審計可能是一個更好的功能,因爲它也會包含SQL文本(我無法從觸發器中獲取)。基本上,我試圖弄清楚爲什麼這些行每個月都會被隨機刪除幾次,即使代碼庫中沒有任何東西從該表中刪除任何內容。 – 2012-01-03 20:25:18

0

我相信你需要在調用RAISE_APPLICATION_ERROR(回滾事務)之前COMMIT INSERT操作。

+2

你不能在觸發器內提交 – 2012-01-03 20:31:18

1

如果您可以在TPM_PROJECTVERSION pk列+版本列上創建一個唯一約束,那麼您可以創建一個引用這些行的第二個表。

試圖刪除TPM_PROJECTVERSION中的一行然後會失敗,因爲子行存在。這至少會在您的應用程序中引發錯誤並阻止刪除。

其他表可以通過TPM_PROJECTVERSION上的插入觸發器自動填充。

如果您撤銷該助手錶上的DELETE權限,則永遠不可能刪除這些行。