停止触发器更改scope_identity

时间:2013-04-08 15:37:16

标签: sql sql-server triggers sql-server-2008-r2

我的应用程序将一些数据插入表中。

insert into this_Table (id, value, value)

然后我创建了一个触发器,它使用主键执行简单插入到另一个表中。

insert into temp_wc_triggertest values ('test', GETDATE())

我的问题是,应用程序尝试从第一个插入中查找scope_identity。但是,它会被触发器覆盖,触发器会将范围标识更改为temp_wc_triggertest的主键。

如何阻止触发器覆盖scope_identity

我意识到这里没有太多代码需要帮助,这通常被归类为一个不好的问题,但我目前无权访问完整的应用程序代码,所以我希望这是一个可以回答的问题。是

这是在SQL Server 2008 R2上

编辑:我查看了代码,确实使用了scope_identity

5 个答案:

答案 0 :(得分:2)

您的客户肯定使用@@IDENTITY而不是SCOPY_IDENTITY()

这是一个SQL小提琴,其中包含一些可以测试的代码。

SQL Fiddle

MS SQL Server 2008架构设置

create table T1(ID int identity(1,1));
create table T2(ID int identity(1000, 1));

go

create trigger tr_T1 on T1 for insert
as
insert into T2 default values;

<强>查询

insert into T1 default values

select @@identity as "@@identity", 
       scope_identity() as "scope_identity()"

<强> Results

| @@IDENTITY | SCOPE_IDENTITY() |
---------------------------------
|       1000 |                1 |

答案 1 :(得分:2)

如果您正确使用SCOPE_IDENTIY,您可能还会遇到已知错误 - http://connect.microsoft.com/SQLServer/feedback/details/328811

MS已将其永久修复为2012年,并且已针对2008和2008R2提供补丁。

答案 2 :(得分:2)

它覆盖范围标识的原因尚不清楚,它可能与提到的错误有关。但是找到了修复:

临时表创建了“temp_wc”

然后在触发器的末尾,为该表启用了标识插入,并且在触发器触发后我们想要保留的ID完成插入。可以将此方法视为重写覆盖的范围标识。

SET IDENTITY_INSERT ON
INSERT INTO temp_wc VALUES (@ID, 'fix scope identity error')

答案 3 :(得分:0)

使用SELECT IDENT_CURRENT(‘tablename’)

“它返回表中生成的最后一个IDENTITY值,无论创建该值的连接如何,也不管产生该值的语句的范围如何。”

有关详细信息,请参阅此链接。

http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-retrieve-last-inserted-identity-of-record/

答案 4 :(得分:0)

这可能会在将来帮助任何人解决此问题:

https://docs.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?view=sql-server-2017

您可能需要修补SQL Server,因为SCOPE_IDENTITY应该返回实际接收主插入而不是SQL触发器插入语句的表上的插入ID。

从SQL Server 2017的Microsoft文档中获取(如果链接断开):

  

备注SCOPE_IDENTITY,IDENT_CURRENT和@@ IDENTITY相似   函数,因为它们返回插入到标识中的值   列。

     

IDENT_CURRENT不受范围和会话的限制;它仅限于   指定表。 IDENT_CURRENT返回为生成的值   任何会话和任何范围的特定表。有关更多信息,请参见   IDENT_CURRENT(Transact-SQL)。

     

SCOPE_IDENTITY和@@ IDENTITY返回的最后一个标识值是   在当前会话的任何表中生成。但是,SCOPE_IDENTITY   返回仅在当前范围内插入的值; @@ IDENTITY为   不限于特定范围。

     

例如,有两个表T1和T2,并且INSERT触发器是   在T1上定义。当在T1中插入一行时,触发器将触发并   在T2中插入一行。此方案说明了两个范围:插入   在T1上,通过触发器在T2上插入。

     

假定T1和T2都具有标识列@@ IDENTITY和   SCOPE_IDENTITY在INSERT的末尾返回不同的值   关于T1的声明。 @@ IDENTITY返回最后一个身份列值   插入当前会话中的任何作用域。这就是价值   插入T2。 SCOPE_IDENTITY()返回插入的IDENTITY值   在T1中。这是同一作用域中发生的最后一次插入。的   如果该函数是SCOPE_IDENTITY()函数,则返回null值   在任何插入身份列的INSERT语句出现之前调用   范围。

     

失败的语句和事务可以更改当前身份   一个表并在标识列值中创建空白。身份   即使试图进行的交易,价值也不会回滚   将值插入表中未提交。例如,如果   由于违反了IGNORE_DUP_KEY,INSERT语句失败,   该表的当前标识值仍会增加。

此处的另一个链接:

http://www.sqlbadpractices.com/how-not-to-retrieve-identity-value/

  

此代码的问题是您可能无法检索到身份   您插入的值。例如,如果   在另一个表上执行插入操作的表,您将获得最后一个   创建身份值。即使您从未创建任何触发器,也可能   由于SQL Server创建了复制的表,因此得到的结果与复制的表有偏差   自己的复制触发器。