我对SQL的了解非常有限,因为我主要关注后端Ruby开发。然而,由于建筑的变化,并希望保持良好的设计;我决定在数据库级别而不是在后端级别设置时间戳。
现在,我的所有表都有两列:CreatedAt
和UpdatedAt
,两者都有默认值GETDATE()
。
但是,我现在需要为UpdatedAt
设置时间戳触发器,以便每次更新一行(或多行)时,这些行的UpdatedAt
列都会获得一个全新的时间戳。
我在编写以下触发器时遇到问题。我收到一个错误:
'='
附近的语法不正确
我首先在Orders表上测试我的触发器,然后我计划将功能移到所有表中。
CREATE TRIGGER dbo.trgTimestampAfterUpdate
ON Dbo.Orders
AFTER UPDATE
AS
BEGIN
IF EXISTS (SELECT * FROM inserted)
BEGIN
SET UpdatedAt = GETDATE()
END
END
我知道在使用触发器时我可以访问插入和删除的虚拟表。我对此查询的想法是,我将使用inserted来区分哪些行已更新。如果有人能提供帮助那就太棒了,而且如果你不介意向我解释我的语法或思路会搞砸了什么,我将不胜感激。
答案 0 :(得分:3)
您无法真正访问插入的表格。刚刚Set UpdatedAt =...
是一个不完整的陈述。隐含地它对你有意义,但即使在你的触发器中,你也必须制作完整的SQL语句。
执行此操作的方法是JOIN到INSERTED表(在下面的示例中,我使用的是半连接)然后,您可以使用INSERTED表的内容执行另一个更新。
CREATE TRIGGER [dbo].[trgTimestampAfterUpdate] ON dbo.orders
FOR UPDATE
AS
BEGIN
IF NOT(UPDATE(UpdatedAt)) --Avoid triggers firing triggers
BEGIN
UPDATE dbo.orders
SET UpdatedAt = GETDATE()
WHERE id IN ( SELECT id
FROM inserted )
END
END
此代码示例中需要注意两个非常重要的事项。首先使用触发器更新表将导致触发器再次触发(创建一个循环,该循环将增加,直到达到系统上嵌套触发器的最大级别。)我检查以确保它终止,如果你是仅更新updatedat
列。
其次,永远不要假设插入的表中只有一行。类似下面的代码是非常常见的错误
DECLARE @id INT
SELECT @id = id FROM INSERTED
UPDATE MyTable
SET UpdatedAT = GETDATE()
WHERE id = @id
--DON'T DO THIS!
这看起来是正确的,并且是一个常见的错误,但它只会更新1条记录,并且INSERTED表中可能有任意数量的记录。