Scope_Identity(),Identity(),@@ Identity和Ident_Current()有什么区别?


191

我知道Scope_Identity()Identity()@@Identity,和Ident_Current()都得到标识列的值,但我很想知道其中的差别。

我遇到的部分争议是它们在适用于以上这些功能时的范围是什么意思?

我还喜欢使用它们的不同场景的简单示例吗?


2
不要忘记SQL Server中针对SCOPE_IDENTITY和@@ IDENTITY的并行执行错误:support.microsoft.com/default.aspx?
David d C e Freitas

@DaviddCeFreitas-我很好奇有关该错误的信息,但是该链接似乎已损坏(或者至少引发了ASP错误)。
rory.ap 2015年

2
实际上,我找到了它:support.microsoft.com/en-us/kb/2019779
rory.ap

如旧的知识库文章所提到的,已发布修复程序
George Birbilis

Answers:


392
  • @@identity函数返回在同一会话中创建的最后一个标识。
  • scope_identity()函数返回在同一会话和相同范围内创建的最后一个标识。
  • ident_current(name)返回的任何会议的特定表或视图中创建的最后一个标识。
  • identity()函数不用于获取身份,而是用于在select...into查询中创建身份。

会话是数据库连接。范围是当前查询或当前存储过程。

一种情形的scope_identity()@@identity功能的不同,就是如果你有在桌子上的触发器。如果您有插入记录的查询,导致触发器在某处插入另一个记录,则该scope_identity()函数将返回查询创建的标识,而@@identity函数将返回由触发器创建的标识。

因此,通常您会使用该scope_identity()功能。


14
我选择了此作为答案,因为“在这种情况下,scope_identity()和@@ identity ...”段落。它使事情更加清晰。
Tebo

1
正如上面提到的David Freitas所说,scope_identity的实现中存在一个错误,因此我建议使用另一种方法OUTPUT子句。请参阅下面的答案。
塞巴斯蒂安·梅因

@Guffa-“会话是数据库连接”。如果使用连接池,会话是否在连接之间维护?
戴夫·布莱克

1
这是一个榜样的答案。特别是,使用SQL和SQL Server可能很奇怪,这以一种非常清晰,通俗易懂的方式解释了事情,同时仍然相当有用。听起来好像两个数据库专家之间没有沟通,很多其他SE答案都可以做到。
Panzercrisis

@DaveBlack从我读到的内容:不,会话不在池中维护,会话对于connect()之后的脚本运行是唯一的。池化时... SQL Server的PHP使用ODBC连接池。使用池中的连接时,将重置连接状态。关闭连接会将连接返回到池。(注:参见用于Linux / MAC备注)docs.microsoft.com/en-us/sql/connect/php/...
GDmac

42

好问题。

  • @@IDENTITY:返回在您的SQL连接(SPID)上生成的最后一个标识值。在大多数情况下,它会是您想要的,但有时不是您想要的(例如,当触发触发器以响应INSERT,并且触发器执行另一条INSERT语句时)。

  • SCOPE_IDENTITY():返回当前范围内生成的最后一个标识值(即存储过程,触发器,函数等)。

  • IDENT_CURRENT():返回特定表的最后一个标识值。请勿使用此方法从中获取身份值INSERT,这取决于竞争条件(即,多个连接在同一张表上插入行)。

  • IDENTITY():在将表中的列声明为标识列时使用。

有关更多参考,请参见:http : //msdn.microsoft.com/zh-cn/library/ms187342.aspx

总结:如果您要插入行,你想知道该行的标识列的值,刚插入,一直使用SCOPE_IDENTITY()


16

如果您了解范围和会话之间的区别,那么将很容易理解这些方法。

Adam Anderson在一篇非常不错的博客文章中描述了这种区别:

会话表示正在执行命令的当前连接。

范围是指命令的直接上下文。每个存储过程调用都在其自己的范围内执行,而嵌套调用则在调用过程的范围内的嵌套范围内执行。同样,从应用程序或SSMS执行的SQL命令在其自己的范围内执行,如果该命令触发任何触发器,则每个触发器在其自己的嵌套范围内执行。

因此,三种身份检索方法之间的差异如下:

@@identity返回在会话中生成的最后一个标识值,但不包括任何范围。

scope_identity()返回在会话和范围中生成的最后一个标识值。

ident_current()返回在任何会话和任何作用域中为特定表生成的最后一个标识值。


11

范围表示执行INSERT语句的代码上下文,SCOPE_IDENTITY()与的全局范围相反@@IDENTITY

CREATE TABLE Foo(
  ID INT IDENTITY(1,1),
  Dummy VARCHAR(100)
)

CREATE TABLE FooLog(
  ID INT IDENTITY(2,2),
  LogText VARCHAR(100)
)
go
CREATE TRIGGER InsertFoo ON Foo AFTER INSERT AS
BEGIN
  INSERT INTO FooLog (LogText) VALUES ('inserted Foo')
  INSERT INTO FooLog (LogText) SELECT Dummy FROM inserted
END

INSERT INTO Foo (Dummy) VALUES ('x')
SELECT SCOPE_IDENTITY(), @@IDENTITY 

给出不同的结果。


9

由于@David Freitas提到的错误以及与2012年引入的新Sequence功能不兼容,我建议您不要使用这三个功能。相反,您可以使用OUTPUT子句来获取插入的标识值。另一个优点是,即使您插入了多行,OUTPUT仍然有效。

有关详细信息和示例,请参见此处:身份危机


我认为这个答案值得更多关注。
cheeze

不幸的是INSERT ... OUTPUT Inserted.xx不能与INSERT触发器一起使用(对于UPDATE ... OUTPUT Updated.xx和UPDATE触发器也是如此)。他们建议使用INSERT ... OUTPUT INTO,但是它太冗长,使用客户端(而不是存储过程)中的那个是有问题的。INSERT ... OUTPUT如果您不需要触发器,则在与客户端调用一起使用时,Inserted.xx很漂亮(只需要ExecuteScalar进行插入,并说回获取新行的自动生成的ID)。
乔治·比比利斯

6

为了澄清问题 @@Identity

例如,如果您插入一个表,并且该表具有执行插入操作的触发器,@@Identity则将从触发器中的插入操作返回ID(a log_id或类似内容),而scope_identity()从原始表中的插入操作返回ID。

所以,如果你没有任何触发器,scope_identity()并且@@identity将返回相同的值。如果有触发器,则需要考虑所需的价值。


4

Scope Identity:正在执行的存储过程中添加的最后一条记录的标识。

@@Identity:在查询批中添加的最后一条记录的标识,或者作为查询的结果(例如执行插入的过程),然后触发触发器,然后插入记录,将从触发器返回插入记录的标识。

IdentCurrent:为表分配的最后一个标识。


3

这是本书中的另一个很好的解释:

至于SCOPE_IDENTITY和@@ IDENTITY之间的区别,假设您有一个包含三个语句的存储过程P1:
-生成新标识值
的INSERT-对存储过程P2的调用,该存储过程P2也具有一个生成新ID的INSERT语句身份值
-查询函数SCOPE_IDENTITY和@@ IDENTITY的语句SCOPE_IDENTITY函数将返回由P1生成的值(相同的会话和范围)。@@ IDENTITY函数将返回由P2生成的值(相同的会话,而不管作用域如何)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.