在触发触发器以响应 INSERT 并且触发器执行另一个 INSERT 语句时使用 @@identity

Posted

技术标签:

【中文标题】在触发触发器以响应 INSERT 并且触发器执行另一个 INSERT 语句时使用 @@identity【英文标题】:using @@identity when a trigger is fired in response to an INSERT, and the trigger executes another INSERT statement 【发布时间】:2011-09-14 12:19:50 【问题描述】:

我有以下表格:

tbl_workshop
id  int  identity
Name nvarchar(10)
Address nvarchar(40)


tbl_workshop_temp
id int identity
code int
InsUpkey  smallint

我有以下陈述

insert into tbl_workshop
    (Name,Address)
     values('x','y')
select @@identity  -- My problem is here

我也有以下插入触发器:

create trigger InsertedWorkshop
on tbl_workshop
for insert
as
  insert into tbl_workshop_temp
     (code,InsUpKey)
    select id,1
        from inserted

select @@identity 语句运行时,我得到tbl_workshop_temp 中插入行的id,而不是id 中插入tbl_workshop

我知道我必须使用scope_identity,但我无法更改代码。我只能改变我的触发器。 我现在该怎么办?请帮帮我。

【问题讨论】:

我能想到一个可行的办法,但你真的应该改变你的代码。为什么无法更改它以调用正确的函数? 因为代码是别人用VB6写的。我可以只更改数据库。 此 SQL 语句在您无权访问的编译代码中是什么?确实,您需要对此进行更改,但假设您正在寻找短期解决方案,我将发布我的(可怕的)想法。 @Raymond 下次不要让“其他人”将糟糕的临时 SQL 放入他们的应用程序 - 鼓励/坚持存储过程! 是的,你是对的。我会做的。 【参考方案1】:

你不能欺骗@@identity 返回一些不同的值。您可以做的是从tbl_workshop_temp 中删除IDENTITY 属性,并且在该列中没有递增值(只需使用与tbl_workshop 相同的ID),完全删除该列,或者在触发器中填充它方式(这意味着它只适用于一行):

DECLARE @id INT;

SELECT @id = MAX(id) + 1
   FROM dbo.tbl_workshop_temp WITH (UPDLOCK, HOLDLOCK); -- important

insert into dbo.tbl_workshop_temp
     (id,code,InsUpKey)
    select @id,id,1
        from inserted;

使用 SQL Server 2005 及更高版本,您可以变得更聪明 - 您可以更改上面的内容,使其使用 ROW_NUMBER() 来处理多行,或者您可以使用 INSTEAD OF TRIGGER 代替(不是双关语)并简单地更改插入语句的顺序。但是对于 SQL Server 2000,您的选择真的很有限...

【讨论】:

+1 如果没有显示任何额外的代码来执行多行插入,这将失败,但是id 可能在tbl_workshop_temp 中是唯一的 @Martin,是的,这是一个重要的免责声明......我将把它添加到答案中......谢谢。 @Aaron Bertan 我更改了您的代码并使用了此触发器。我做得对吗? create trigger InsertedWorkshop on tbl_workshop for insert as declare @id int select @id=id from inserted insert into tbl_workshop_temp (id,code,insupkey) select @id,id,1 from inserted 我只想在我的id 字段中有一个唯一的键,所以我使用了tbl_workshopid。两个用户同时插入两条记录不会有问题吗? 如果表为空COALESCE(MAX(id),0) + 1更好;否则你会得到 NULL 的 id。 @JustAMartin 是的,这里的答案很简单,因为我们可以假设在大多数情况下,像这样的表格不会突然从有行变成完全空的。【参考方案2】:

请参阅this question,了解每个身份选择的工作原理。您将不得不更改初始 INSERT 的代码才能修复此问题。 @@identity 总是从会话中获取最后一个 id,并且由于触发器是同一会话的一部分,因此您必须更改在应用程序中获取 ID 的方式。

【讨论】:

【参考方案3】:

你可以像这样使用 where 子句在插入的行中选择

SELECT ID_FIRST_TABLE FROM TRIGGERED_TABLE WHERE TRIGGERED_ID = @@Identity

【讨论】:

以上是关于在触发触发器以响应 INSERT 并且触发器执行另一个 INSERT 语句时使用 @@identity的主要内容,如果未能解决你的问题,请参考以下文章

触发器

SQL Server - 在模式中触发AFTER INSERT表

MySQL ------ 触发器(TRIGGER)(二十七)

触发器

PostgreSQL 创建触发器 Trigger

《mysql必知必会》读书笔记--触发器及管理事务处理