在一条 insert、select into 或大容量复制语句完成后,@@identity 中包含语句生成的最后一个标识值。如果语句未影响任何包含标识列的表,则 @@identity 返回 null。如果插入了多个行,生成了多个标识值,则 @@identity 将返回最后生成的标识值。如果语句触发了一个或多个触发器,该触发器又执行了生成标识值的插入操作,那么,在语句执行后立即调用 @@identity 将返回触发器生成的最后一个标识值。如果对包含标识列的表执行插入操作后触发了触发器,并且触发器对另一个没有标识列的表执行了插入操作,则 @@identity 将返回第一次插入的标识值。出现 insert 或 select into 语句失败或大容量复制失败,或者事务被回滚的情况时,@@identity 值不会恢复为以前的设置。
如果语句和事务失败,它们会更改表的当前标识,从而使标识列中的值出现不连贯现象。即使未提交试图向表中插入值的事务,也永远无法回滚标识值。例如,如果因 ignore_dup_key 冲突而导致 insert 语句失败,表的当前标识值仍然会增加。
@@identity、scope_identity 和 ident_current 是相似的函数,因为他们都返回插入到表的 identity 列的最后一个值。
@@identity 和 scope_identity 可以返回当前会话中的所有表中生成的最后一个标识值。但是,scope_identity 只在当前作用域内返回值,而 @@identity 不限于特定的作用域。
ident_current 不受作用域和会话的限制,而受限于指定的表。ident_current 可以返回任何会话和任何作用域中为特定表生成的标识值。
@@identity 函数的作用域是执行该函数的本地服务器上的当前会话。此函数不能应用于远程或链接服务器。若要获得其他服务器上的标识值,请在远程服务器或链接服务器上执行存储过程,并使(在远程或链接服务器的环境中执行的)该存储过程收集标识值,并将其返回本地服务器上的发出调用的连接。
以下示例向包含标识列 (locationid) 的表中插入一行,并使用 @@identity 显示新行中使用的标识值:
use adventureworks;go--display the value of locationid in the last row in the table.select max(locationid) from production.location;goinsert into production.location (name, costrate, availability, modifieddate)values ('damaged goods', 5, 2.5, getdate());goselect @@identity as 'identity';go--display the value of locationid of the newly inserted row.select max(locationid) from production.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 是取全局操作的最后一步操作所产生的自增域的值的。