插入后使用 SQL Server 触发器更新另一个表中的列

Posted

技术标签:

【中文标题】插入后使用 SQL Server 触发器更新另一个表中的列【英文标题】:Use SQL Server trigger to update a column in another table after an insert 【发布时间】:2018-06-02 08:05:20 【问题描述】:

我正在尝试使用 SQL Server 触发器完成以下操作:一旦将新行插入到表中,我想更新另一个表中的列。

例如:我有一个名为Users 的表和一个名为Activity 的表。每次客户端连接到服务器时,都会在Activity 表中插入一行;插入行后(包含user_id),我想更新Users 表中存储上次活动的日期和时间的列。

UPDATE Users
SET Last_Activity_TimeStamp = GETDATE()
WHERE Users.User_ID = Activity.User_ID

但是 SSMS 抛出一个错误说:

无法绑定多部分标识符“dbo.Activity.User_ID”

如果我把上面的更新语句改成:

UPDATE Users
SET Last_Activity_TimeStamp = GETDATE()
WHERE Users.User_ID = User_ID

Users 表中的所有行都更新了。

我知道有一个 Inserted 表,并且这些行是在插入到主 Activity 表之前添加到那里的,但是我可能有很多用户正在连接,并且会有很多行插入到Activity 表。可能每秒 1000 次。我想知道如何将要插入的行的user_id 插入Activity 表并更新Users 表中的Last_Activity_TimeStamp

编辑:我想我缺乏知识的地方是我不知道任何时候Inserted 表中可以有多少行,因为在我的设计中,单个事务只插入一行。由于有多个用户连接和断开连接,是否有可能在Inserted 表中存在来自其他活动事务的不止一行?

任何帮助将不胜感激!

谢谢

【问题讨论】:

非常感谢@marc_s 的样式编辑,现在更好更清晰了。 【参考方案1】:

其他响应都没有考虑到触发器中的Inserted 伪表有时会包含多个行 - 应该永远不要选择单个value - 如果一次插入多行,这将不工作

触发器必须以基于集合的方式编写来处理这种情况 - 这真的很容易 - 尝试这样的事情:

CREATE TRIGGER trgActivityInsert
ON [dbo].[Activity]
FOR INSERT
AS
BEGIN
    UPDATE u
    SET Last_Activity_TimeStamp = GETDATE()
    FROM dbo.Users u
    INNER JOIN Inserted i ON u.User_Id = i.User_Id
END

这样,无论您一次插入一行、十行还是一百行,您的触发器都可以正常工作,并且dbo.User 表中的所有相应条目都将正确更新。

【讨论】:

太好了,我已经设法做了一些测试,你是绝对正确的。我唯一要补充的是,正如上面发布的那样,似乎 Inserted 表的范围仅限于触发器,并且由于设计上我只为每个用户的每个活动插入一行(永远不会更多),因此它将只包含一行。不过话虽如此,你的方法确实是最有效的方法,所以我将采用它。非常感谢!【参考方案2】:

您不能引用 dbo.Activity.User_ID 等表字段。请像这样更改您的触发代码..

CREATE TRIGGER Trk_Test 
    ON [dbo].[Activity]
    FOR INSERT
AS
BEGIN
    DECLARE @Id INT = 0

    SELECT @ID = [User_Id] FROM inserted

    UPDATE Users 
    SET Last_Activity_TimeStamp = GETDATE()
    WHERE [User_Id] = @Id

END

插入和删除的表格 Inserted / Deleted 是仅对触发器具有作用域的虚拟表。你不能在触发器之外引用。因此,无论一次连接多少用户,插入和删除的表都不会发生冲突。

【讨论】:

您的触发器有 MAJOR 缺陷,因为您似乎认为它会每行调用一次 - 那是不是 b> 情况。触发器将每条语句触发一次,因此如果您的INSERT 语句导致此触发器触发插入 25 行,您将触发触发器一次,但随后Inserted 伪表将包含 25 行。您的代码将在这 25 行中选择哪一行? SELECT @ID = [User_Id] FROM inserted - 它是不确定的,您将获得任意行,您将忽略所有其他行。您需要重写触发器以考虑到这一点! 是的..你是对的@marc_s..但是在上面的问题中听到,如上所述,单个事务中只会插入一行...... 是的 - 现在,OP 说他只插入了行 - 但将来可能会改变。不要编写仅在某些情况下有效的代码 - 特别是如果编写正确的代码如此简单(如我自己的答案所示) 好的.. 谢谢@marc_s

以上是关于插入后使用 SQL Server 触发器更新另一个表中的列的主要内容,如果未能解决你的问题,请参考以下文章

如何在使用触发器的 SQL 插入后更新另一个表

创建触发器后的 SQL Server

插入和更新后未触发 SQL Server 触发器

SQL Server在复制时触发

在 Microsoft SQL Server 上插入触发器后 -- 更新新创建记录中的列

Sql Server 2005 - 插入更新触发器 - 获取更新,插入行