zl程序教程

您现在的位置是:首页 >  其它

当前栏目

@@IDENTITY与SCOPE_IDENTITY()

Scope identity
2023-06-13 09:14:40 时间

在一条INSERT、SELECTINTO或大容量复制语句完成后,@@IDENTITY中包含语句生成的最后一个标识值。如果语句未影响任何包含标识列的表,则@@IDENTITY返回NULL。如果插入了多个行,生成了多个标识值,则@@IDENTITY将返回最后生成的标识值。如果语句触发了一个或多个触发器,该触发器又执行了生成标识值的插入操作,那么,在语句执行后立即调用@@IDENTITY将返回触发器生成的最后一个标识值。如果对包含标识列的表执行插入操作后触发了触发器,并且触发器对另一个没有标识列的表执行了插入操作,则@@IDENTITY将返回第一次插入的标识值。出现INSERT或SELECTINTO语句失败或大容量复制失败,或者事务被回滚的情况时,@@IDENTITY值不会恢复为以前的设置。

     如果语句和事务失败,它们会更改表的当前标识,从而使标识列中的值出现不连贯现象。即使未提交试图向表中插入值的事务,也永远无法回滚标识值。例如,如果因IGNORE_DUP_KEY冲突而导致INSERT语句失败,表的当前标识值仍然会增加。

    @@IDENTITY、SCOPE_IDENTITY和IDENT_CURRENT是相似的函数,因为他们都返回插入到表的IDENTITY列的最后一个值。

    @@IDENTITY和SCOPE_IDENTITY可以返回当前会话中的所有表中生成的最后一个标识值。但是,SCOPE_IDENTITY只在当前作用域内返回值,而@@IDENTITY不限于特定的作用域。

    IDENT_CURRENT不受作用域和会话的限制,而受限于指定的表。IDENT_CURRENT可以返回任何会话和任何作用域中为特定表生成的标识值。

    @@IDENTITY函数的作用域是执行该函数的本地服务器上的当前会话。此函数不能应用于远程或链接服务器。若要获得其他服务器上的标识值,请在远程服务器或链接服务器上执行存储过程,并使(在远程或链接服务器的环境中执行的)该存储过程收集标识值,并将其返回本地服务器上的发出调用的连接。

    以下示例向包含标识列(LocationID)的表中插入一行,并使用@@IDENTITY显示新行中使用的标识值:

复制代码代码如下:

USEAdventureWorks;
GO
--DisplaythevalueofLocationIDinthelastrowinthetable.
SELECTMAX(LocationID)FROMProduction.Location;
GO
INSERTINTOProduction.Location(Name,CostRate,Availability,ModifiedDate)
VALUES("DamagedGoods",5,2.5,GETDATE());
GO
SELECT@@IDENTITYAS"Identity";
GO
--DisplaythevalueofLocationIDofthenewlyinsertedrow.
SELECTMAX(LocationID)FROMProduction.Location;
GO

补充:
  我们要慎用@@IDENTITY,原因是@@IDENTITY它总是获取最后一条变更数据的自增字段的值,
 而忽略了进行变更操作所在的范围约束。比如,我有表A和表B两个表,现在我在表A上定义了一个Insert触发器,当在表A中插入一条数据时,自动在表B也插入一条数据。此时,大家注意,有两个原子操作:在A中插入一条数据,接着在B中随后插入一条数据。

   现在我们想下,假设上面表A和表B都有IDENTITY自增域,那么我们在表A插入一条数据后,使用了SELECT@@IDENTITY输出时,输出的到底是A还是B的自增域的值呢? 答案很明显,是谁最后插入就输出谁,那么就是B了。于是,我本意是想得到A的自增域值,结果得到了B的自增域值,一只BUG随之诞生,搞不好还会影响到整个系统数据的混乱。

   因此,对于这种情况,建议大家慎用@@IDENTITY,而尽量采用SCOPE_IDENTITY()函数替换之。SCOPE_IDENTITY()也是得到最后一条自增域的值,但是它是仅限在一个操作范围之内,而不@@IDENTITY是取全局操作的最后一步操作所产生的自增域的值的。