2

我有具有bit柱和相應的datetime2列時標誌設置跟蹤表:檢查約束違反之前更新後觸發器觸發

CREATE TABLE MyTable 
(
    Id int primary key identity, 
    Processed bit not null, 
    DateTimeProcessed datetime2 
) 

我添加了一個檢查約束,如下所示:

ALTER TABLE MyTable 
    ADD CHECK ((Processed = 0 AND DateTimeProcessed IS NULL) 
      OR (Processed = 1 AND DateTimeProcessed IS NOT NULL)) 

我試圖使用AFTER UPDATE觸發信號以控制DateTimeProcessed柱的設置:

CREATE TRIGGER tr_MyTable_AfterUpdate ON MyTable 
AFTER UPDATE 
AS 
BEGIN 
    IF(UPDATE(Processed)) 
    BEGIN 
     UPDATE MyTable 
     SET DateTimeProcessed = CASE 
      WHEN tab.Processed = 1 THEN GETDATE() 
      ELSE NULL 
      END 
     FROM MyTable tab 
     JOIN INSERTED ins 
     ON ins.Id = tab.Id 
    END 
END 

問題在於檢查約束在AFTER UPDATE觸發器運行之前執行,因此在更新Processed列時違反了約束條件。

什麼會達到什麼樣的,我想在這裏做的最好方法是什麼?現在

回答

2

,根據MSDN頁CREATE TABLE

如果表的外鍵或檢查約束和觸發器,所述約束條件是在執行觸發前評估。

這排除了使用「INSTEAD OF」觸發器的可能性。

您應該刪除作爲最終它不需要檢查約束,因爲觸發後本身所能提供的規則相同執法:

  • 您已經確保日期場被於集BIT字段設置爲1。
  • 你的CASE語句已經歸零了日期字段處理位字段被設置爲0。
  • 您可以使用另一個塊檢查IF UPDATE(DateTimeProcessed),並將其恢復爲DELETED表中的內容或發生錯誤。

    • 如果您將其更新回原始值,則可能需要測試遞歸觸發器調用,如果它是遞歸調用則退出。
    • 如果你想拋出一個錯誤,只是使用的線沿線的東西:

      IF(UPDATE(DateTimeProcessed)) 
      BEGIN 
          RAISERROR('Update of [DateTimeProcessed] field is not allowed.', 16, 1); 
          ROLLBACK; -- cancel the UPDATE statement 
          RETURN; 
      END; 
      

      記住的UPDATE()函數只表示該場是在UPDATE語句;它是的值變更的指示。因此,做一個更新,其中你SET DateTimeProcessed = DateTimeProcessed顯然不會改變的價值,而且還會引起UPDATE(DateTimeProcessed)返回「真」。

    • 您也可以通過使用列級DENY處理觸發的「規則」之外的這部分:

      DENY UPDATE ON MyTable (DateTimeProcessed) TO {User and/or Role};

+1

「的評論說,建議」 BEFORE 「觸發器可能由於SQL Server沒有」BEFORE「觸發器而被刪除」......是的,這可能是原因,不是嗎:) – grin0048 2014-11-05 20:34:08

+0

感謝您的答案。如果直接更新'DateTimeProcessed'則拋出一個錯誤,就像在這裏添加的最後一部分。 – grin0048 2014-11-05 20:44:47

+0

@ grin0048:是的,我曾建議在這種情況下拋出一個錯誤,那是你指的還是你沒有看到我的更新?無論如何,我再次更新以重新排列項目符號的順序並添加了一些細節,例如處理錯誤和取消的代碼以及不涉及代碼的新(不太知名)選項。 – 2014-11-05 22:02:00