Scope_Identity()、Identity()、@@Identity 和 Ident_Current() 有啥区别?
Posted
技术标签:
【中文标题】Scope_Identity()、Identity()、@@Identity 和 Ident_Current() 有啥区别?【英文标题】:What is the difference between Scope_Identity(), Identity(), @@Identity, and Ident_Current()?Scope_Identity()、Identity()、@@Identity 和 Ident_Current() 有什么区别? 【发布时间】:2009-12-17 09:42:55 【问题描述】:我知道Scope_Identity()
、Identity()
、@@Identity
和Ident_Current()
都获得了标识列的值,但我很想知道它们的区别。
我遇到的部分争议是适用于上述这些功能的范围是什么意思?
我也想要一个使用它们的不同场景的简单示例?
【问题讨论】:
不要忘记 SQL Server 中存在的 SCOPE_IDENTITY 和 @@IDENTITY 的并行执行错误:support.microsoft.com/default.aspx?scid=kb;en-US;2019779 @DaviddCeFreitas -- 我很想知道这个错误,但链接似乎已损坏(或者至少,它抛出了一个 ASP 错误)。 其实我找到了:support.microsoft.com/en-us/kb/2019779 如旧知识库文章中所述,已发布修复 【参考方案1】:@@identity
函数返回在同一会话中创建的最后一个身份。
scope_identity()
函数返回在同一会话和同一范围内创建的最后一个身份。
ident_current(name)
返回在任何会话中为特定表或视图创建的最后一个标识。
identity()
函数不用于获取身份,它用于在 select...into
查询中创建身份。
会话是数据库连接。范围是当前查询或当前存储过程。
scope_identity()
和 @@identity
函数不同的情况是,如果您在桌子上有触发器。如果您有一个插入记录的查询,导致触发器在某处插入另一条记录,scope_identity()
函数将返回查询创建的标识,而@@identity
函数将返回触发器创建的标识。
所以,通常你会使用scope_identity()
函数。
【讨论】:
我之所以选择这个作为答案,是因为“scope_identity() 和 @@identity... 的情况”段落。它进一步澄清了事情。 正如上面的 David Freitas 所说,scope_identity 的实现存在一个错误,所以我建议使用另一种方法,即 OUTPUT 子句。请参阅下面的答案。 @Guffa - “会话是数据库连接”。如果您使用连接池,会话是否跨连接维护? 这是一个榜样的答案。特别是,使用 SQL 和 SQL Server 可能会很奇怪,这以非常清晰、外行的方式解释了事情,同时仍然提供了相当丰富的信息。这听起来不像是在两个数据库专家之间交流的东西,很多其他 SE 的答案都是这样。 @DaveBlack 从我读到的内容:不,会话不在池中维护,会话对于您的脚本在 connect() 之后运行是唯一的。当池... php for SQL Server 使用 ODBC 连接池。当使用池中的连接时,连接状态被重置。关闭连接会将连接返回到池。 (注:linux/mac 见备注)docs.microsoft.com/en-us/sql/connect/php/…【参考方案2】:好问题。
@@IDENTITY
:返回在您的 SQL 连接 (SPID) 上生成的最后一个标识值。大多数时候它会是你想要的,但有时它不是(比如触发触发器以响应INSERT
,并且触发器执行另一个INSERT
语句)。
SCOPE_IDENTITY()
:返回当前作用域(即存储过程、触发器、函数等)生成的最后一个标识值。
IDENT_CURRENT()
:返回特定表的最后一个标识值。不要使用它从 INSERT
获取标识值,它受竞争条件(即多个连接在同一个表上插入行)的影响。
IDENTITY()
:在将表中的列声明为标识列时使用。
如需更多参考,请参阅:http://msdn.microsoft.com/en-us/library/ms187342.aspx。
总结一下:如果您正在插入行,并且您想知道刚刚插入的行您的标识列的值,请始终使用SCOPE_IDENTITY()
。
【讨论】:
【参考方案3】:如果你了解范围和会话之间的区别,那么理解这些方法将非常容易。
Adam Anderson 的一个非常好的 blog post 描述了这种差异:
会话表示正在执行命令的当前连接。
作用域 表示命令的直接上下文。每个存储过程调用都在其自己的范围内执行,嵌套调用在调用过程范围内的嵌套范围内执行。同样,从应用程序或 SSMS 执行的 SQL 命令在其自己的范围内执行,如果该命令触发任何触发器,则每个触发器在其自己的嵌套范围内执行。
因此三种身份检索方式的区别如下:
@@identity
返回在 this 会话中生成的最后一个标识值,但 any 范围。
scope_identity()
返回在 this 会话和 this 范围内生成的最后一个标识值。
ident_current()
返回为 any 会话和 any 范围内的特定表生成的最后一个标识值。
【讨论】:
【参考方案4】:范围是指执行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
给出不同的结果。
【讨论】:
【参考方案5】:澄清@@Identity
的问题:
例如,如果您插入一个表并且该表具有执行插入操作的触发器,@@Identity
将返回触发器中插入的 id(log_id
或其他东西),而 scope_identity()
将返回来自在原始表中插入。
因此,如果您没有任何触发器,scope_identity()
和 @@identity
将返回相同的值。如果你有触发器,你需要考虑你想要什么值。
【讨论】:
【参考方案6】:Scope Identity
: 在正在执行的存储过程中添加的最后一条记录的标识。
@@Identity
:在查询批次中添加的最后一条记录的标识,或者作为查询的结果,例如一个执行插入的过程,然后触发一个触发器,然后插入一条记录将从触发器返回插入记录的标识。
IdentCurrent
:为表分配的最后一个标识。
【讨论】:
【参考方案7】:这是the book的另一个很好的解释:
至于SCOPE_IDENTITY和@@IDENTITY的区别,假设你有一个存储过程P1,包含三个语句: - 生成新标识值的 INSERT - 对存储过程 P2 的调用,该存储过程 P2 也具有生成新标识值的 INSERT 语句 - 查询函数 SCOPE_IDENTITY 和 @@IDENTITY 的语句 SCOPE_IDENTITY 函数将返回 P1 生成的值(相同的会话和范围)。 @@IDENTITY 函数将返回 P2 生成的值(无论范围如何,都是相同的会话)。
【讨论】:
以上是关于Scope_Identity()、Identity()、@@Identity 和 Ident_Current() 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
Scope_Identity()、Identity()、@@Identity 和 Ident_Current() 有啥区别?