我有三个表,“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 :(得分: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 父母 更新后 如 如果没有更新(updatedAt) 更新父母 SET updatedAt = CONVERT(DATETIMEOFFSET(3),SYSUTCDATETIME()) 来自插入 WHERE Parent.id = Inserted.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的触发器。