2016-02-11 306 views
0

我有一個SQL觸發器的問題。這是一個「AFTER INSERT」觸發器。它適用於每個IF EXISTS塊,除了具有更新和引發錯誤組合的塊,如下所示。它要麼更新並繼續前進,要麼停止更新。SQL觸發器 - 插入後 - Update語句和RAISERROR不工作

代碼:(我已關閉 - 在它的多個不同的嘗試,他們都失敗了)

IF EXISTS (SELECT 
        [RPS].[Slip] 
       FROM 
        [DC].[dbo].[Slips] AS [RPS] 
       WHERE 
        [RPS].[Slip] = @ps 
        AND [RPS].[Status] = 0) 
       BEGIN  
        BEGIN TRAN; 
          UPDATE 
           [DC].[dbo].[Slips] 
          SET 
           [Slip].[Status] = 1 
          FROM 
           [DC].[dbo].[Slips] 
          WHERE 
           [Slips].[Slip] = @ps; 

         SET @msg = ' ' + @NewLine + 'Inv. Decremented - Rollback' + @NewLine + 'Contact HD.' + @NewLine; 

         RAISERROR (@msg,16,1); 
         COMMIT TRAN; 
         RETURN; 
       END; 

的目標是表更新爲1時,如果存在則是抓住並觸發狀態RAISERROR。 RAISERROR被java代碼拾取並停止處理。如果我更新了該觸發器,則會引發錯誤並停止。如果我把加註錯誤拿出來,觸發器會更新但會繼續 - 我不想要那個...我想要我的蛋糕並且也吃它!

想法?

+0

觸發器啓動一個隱式事務,所以你在觸發器中的顯式事務實際上是一個嵌套事務。 SQL Server不尊重嵌套事務,並且可能在那裏發生了一些奇怪的事情。我只是刪除你的明確交易,看看它是否適合你。 –

+2

更正以上評論。這並不是說SQL Server「不尊重嵌套事務」,但語義可能與你期望的有所不同。 SQL Server跟蹤'@@ trancount'中的嵌套級別,並且只有外部'commit'(導致'@@ trancount'從1變爲0的那個)將實際提交事務。然而,回滾(沒有保存點)會回滾一個以「@@ trancount」爲0的事務。 –

+2

BEGIN TRAN是多餘的。它可以安全地刪除而不影響觸發器的行爲,因爲@JohnSpecko指出已經有一個隱式事務。然而,我不指望這能解決你遇到的任何問題。 [DC]。[dbo]。[Slips]是否有更新觸發器,並且該更新觸發器中是否有任何可能的ROLLBACK語句?可能發生的情況是,ROLLBACK正在回滾所有嵌套事務,包括你的工作。 –

回答

2

爲了防止更新底層觸發表,同時允許更新到滑動表,您需要一個INSTEAD OF INSERT觸發器,一個VIEW和一個所有權鏈,以防止用戶或其應用程序直接修改基礎表碼。由於之前在註釋中提到的原因,您無法使用傳統的AFTER UPDATE觸發器,因爲觸發插入和觸發的更新都包裝在同一個事務中,它們必須同時生效或不生效。您不能僅提交事務的一部分,並且嵌套事務是它所包含的最外層事務的一部分。

現在的解決方案...

第一部分 - 查看 創建具有相同的列結構和命名爲具有插入觸發器是給你這一切麻煩的表的視圖。形式的東西:

CREATE VIEW [same-name-as-the-table-you-are-using] 
AS 
    SELECT <list-all-columns-explicitly-please-dont-use-star> 
    FROM <original-table-with-slightly-different-name-now> 

如果原始表被命名爲XXX然後重命名它像XXX_SINK和使用XXX的視圖。用戶和應用程序開發人員應將此視圖視爲他們正在使用的「表格」。

第2部分 - 所有權鏈 當引用對象和引用對象具有相同所有者時,在SQL Server中建立所有權鏈。當不同的一方(不是所有者)訪問引用對象(在這種情況下,視圖),用於該方的權限對引用對象像往常一樣評價,但是未評價與被參考的對象(在這種情況下,表)。自從SQL Server開始以來,這一直是SQL Server的一項功能,但它並未被衆多SQL開發人員所瞭解或很好理解。你可以得到更多關於ownership chains here的信息。

您將要拒絕權限的用戶基礎表,並將其授予視圖。這意味着用戶只能通過視圖插入或更新行,而不能直接插入表中。因爲你不希望他們繞過你將在接下來的部分怎麼辦...

3部分做到這一點是重要的 - INSTEAD OF觸發器 對視圖創建INSTEAD OF觸發。其語法與AFTER觸發器的語法類似,只不過INSTEAD OF出現在AFTER的位置,並且在被觸發的位置沒有發生任何插入操作,除非觸發器本身對「接收器」表進行更新,否則不會更新將完全執行。這個觸發器可以混合和匹配任何它想要的。與AFTER觸發器一樣,存在隱式事務,但僅執行觸發器本身的顯式數據修改操作。

請記住,觸發器必須明確執行insert到底層匯表中。可以從inserted特殊表中檢索要插入的行,就像AFTER觸發器的情況一樣。請記住,有可能(至少在SQL Server中)至少要插入一行(實際上在零行插入語句中可以有零行)。您需要決定是否允許好行或拒絕插入所有行。鑑於你的要求,我懷疑後者。

聲音數據庫設計的問題,我會強烈反對 -

  1. 觸發限制插入到單排和
  2. 使用任何形式的遊標的觸發器內處理多行插入。改用面向集合的DML。

即使當前應用程序可能一次只能插入一行,但數據庫不應該強加這樣的限制。

合理的RAISERROR(這是一個合理的嚴重性和狀態值)不會導致任何事情被中止或回滾。

在這種組合中做這些事情應該會產生你想要的結果。