2014-03-01 69 views
0

我有三個表,「elements」表中的行屬於「items」表中的一行,而該行又屬於「categories」表。 現在,我已經得到了觸發設置在每個表的更新上插入或更新時間戳(updatedAt):如何「級聯」更新時間戳(使用觸發器)

CREATE TRIGGER [Category_InsertUpdateDelete] ON [Category] 
    AFTER INSERT, UPDATE, DELETE 
AS 
BEGIN 
    SET NOCOUNT ON; 
    IF TRIGGER_NESTLEVEL() > 3 RETURN; 

    UPDATE [Category] SET [Category].[updatedAt] = CONVERT (DATETIMEOFFSET(3), SYSUTCDATETIME()) 
    FROM INSERTED 
    WHERE INSERTED.id = [Category].[id] 
END 

現在我試圖更新父行的時間戳這樣:

CREATE TRIGGER [Item_InsertUpdateDelete] ON [Item] 
    AFTER INSERT, UPDATE, DELETE 
AS 
BEGIN 
    SET NOCOUNT ON; 
    IF TRIGGER_NESTLEVEL() > 3 RETURN; 

    DECLARE @updatedAt DATETIMEOFFSET(3) = CONVERT(DATETIMEOFFSET(3), SYSUTCDATETIME()); 

    UPDATE [Item] SET [Item].[updatedAt] = @updatedAt 
    FROM INSERTED 
    WHERE INSERTED.id = [Item].[id] 

    UPDATE [Category] SET [Category].[updatedAt] = @updatedAt 
    FROM INSERTED 
    WHERE INSERTED.categoryId = [Category].[id] AND [Category].[updatedAt] < @updatedAt; 
END 

有雖然兩個問題:
1)它是造成死鎖,作爲該項目的觸發似乎在等待類別觸發,都希望更新的類別。
2)更新後的時間戳類別與項目時間戳記不同,因爲類別的觸發器會再次更改它(差異爲毫秒)。

我雖然使用UPDATE()函數來檢查updatedAt列是否在類別觸發器中發生了更改,但我不清楚這是否適用於批量插入/更新。檢查TRIGGER_NESTLEVEL是否可能導致這種「級聯」的特定觸發器,如果​​它返回的工作量超過0,則只是返回?

做這個時間戳的「級聯」的最佳方式是什麼?

在此先感謝!

回答

0

如果您沒有真正嘗試更新鏈上的相同時間戳字段,系統可能會更簡單。如果您確實需要頂部提供的所有信息,那麼您可以分別跟蹤Category.updateAt,Category.updateAtItem和Category.updateAtElement。您可以爲任何級別的最新更新添加一個計算(可能是持久)列。

或者,您可以引用一個視圖,該視圖加入了關卡並提供了「正確的」updateAt。如果你不太經常需要這些信息,這可能會發生。

但是,如果這些更改失敗,請嘗試檢查以查看updateAt字段不是正在更新的字段。所以在Category_InsertUpdateDelete中,如果不是UPDATE(updateAt),當它是一個級聯觸發器時決定退出。

我也懷疑你是在處理DELETE。您可能想要將DELETE邏輯分離爲單獨的觸發器。

編輯:

下面是一個視圖的簡化的例子,我可能會嘗試對大小承諾級聯之前觸發:

CREATE VIEW ParentChildUpdateAt AS 
    SELECT id 
     ,CASE WHEN updatedAt >= updatedAt_Child THEN updatedAt 
       ELSE updatedAt_Child 
      END updatedAt 
    FROM Parent 
      CROSS APPLY (
      SELECT MAX(updatedAt) updatedAt_Child 
       FROM Child 
       WHERE Parent.Id = Child.Parent_id) Child 
GO 

這裏是如何使用UPDATE()的例子避免問題的功能。請注意,使用默認值可以簡化插入的觸發器。

CREATE TABLE Parent(
    id INT 
    ,data INT 
    ,updatedAt DATETIMEOFFSET(3) NOT NULL DEFAULT CONVERT (DATETIMEOFFSET(3), SYSUTCDATETIME()) 
) 
GO 
CREATE TABLE Child(
    id INT 
    ,Parent_id INT 
    ,data INT 
    ,updatedAt DATETIMEOFFSET(3) NOT NULL DEFAULT CONVERT (DATETIMEOFFSET(3), SYSUTCDATETIME()) 
) 
GO 

CREATE TRIGGER Parent_Update ON父 UPDATE AS 之後,如果不更新(updatedAt) 更新父 SET updatedAt = CONVERT(DATETIMEOFFSET(3),SYSUTCDATETIME()) FROM INSERTED WHERE父。 id =插入。ID GO

CREATE TRIGGER Child_Insert 
      ON Child 
     AFTER INSERT 
AS BEGIN 

    UPDATE Parent 
    SET updatedAt = INSERTED.updatedAt 
    FROM INSERTED 
    WHERE Parent.id = INSERTED.Parent_id 
    AND INSERTED.updatedAt > Parent.updatedAt 

END 
GO 

CREATE TRIGGER Child_Update 
      ON Child 
     AFTER UPDATE 
AS BEGIN 

    DECLARE @dt DATETIMEOFFSET(3) = CONVERT (DATETIMEOFFSET(3), SYSUTCDATETIME()) 

    IF UPDATE(updatedAt) 
    SELECT @dt = updatedAt 
     FROM INSERTED 
    ELSE BEGIN 
    SET @dt = CONVERT (DATETIMEOFFSET(3), SYSUTCDATETIME()) 
    UPDATE Child 
     SET updatedAt = @dt 
     FROM INSERTED 
    WHERE Child.id = Inserted.id 
    END 

    UPDATE Parent 
    SET updatedAt = @dt 
    FROM INSERTED 
    WHERE Parent.id = INSERTED.Parent_id 
    AND @dt > Parent.updatedAt 

END 
GO 

你可以看看重組使用方法孩子插入和更新討論here

觸發Child_Delete將只需要更新父日期。 Parent_Delete的觸發器是不必要的。

+0

嗯,這並不是真的有必要有這麼多的信息。只需要知道該行或其子代的最後更新時間。 我猜一個視圖不是一個選項;它用了很多。 UPDATE(updatedAt)函數如何在批量更新中工作? (我對此不確定)。在我看來,這將是最好的選擇。 將它們分開的優點是什麼? (我對觸發器不熟悉)顯然這段代碼對DELETE沒有用處。 – Alexander

+0

@亞歷山大:我已經用例子更新了我的答案。 –

+0

非常感謝!這些例子很棒。 – Alexander