将最后一个身份插入表中的最佳方法

AA.*_*.SC 42 sql-server sql-server-2008-r2 identity

哪个是获取我刚刚通过插入生成的标识值的最佳选择?这些语句对性能有什么影响?

  1. SCOPE_IDENTITY()
  2. 聚合函数 MAX()
  3. SELECT TOP 1IdentityColumn FROM TableNameORDER BY IdentityColumn DESC

Aar*_*and 64

SCOPE_IDENTITY()如果您插入单行并希望检索生成的ID。

CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1));

INSERT #a(x) VALUES('a');

SELECT SCOPE_IDENTITY();
Run Code Online (Sandbox Code Playgroud)

结果:

----
1
Run Code Online (Sandbox Code Playgroud)

OUTPUT如果您要插入多行并需要检索集合,请使用该子句生成的 ID,。

INSERT #a(x) 
  OUTPUT inserted.identity_column 
  VALUES('b'),('c');
Run Code Online (Sandbox Code Playgroud)

结果:

----
2
3
Run Code Online (Sandbox Code Playgroud)

为什么这是最好的更快的选择?

除了性能之外,这些是唯一可以保证在默认隔离级别和/或多个用户下正确的。即使您忽略正确性方面,SQL Server 也会将插入的值保存在SCOPE_IDENTITY()也会将在内存中,因此这自然会比针对表或系统表运行您自己的隔离查询更快。

忽略正确性方面就像告诉邮递员他今天的邮件投递做得很好——他比平均时间快了 10 分钟完成了他的路线,问题是,没有一封邮件被投递到正确的房子。

请勿使用以下任何一种:

  • @@IDENTITY - 因为这不能在所有场景中使用,例如,当一个带有标识列的表有一个触发器,该触发器也插入到另一个具有自己标识列的表中时 - 你会得到错误的值。
  • IDENT_CURRENT()- 我在这里详细介绍,评论也很有用,但本质上,在并发情况下,您经常会得到错误的答案。
  • MAX()或者TOP 1- 您必须使用可序列化的隔离来保护这两个语句,以确保MAX()您得到的不是别人的。这比仅使用SCOPE_IDENTITY().

每当您插入两行或更多行时,这些函数也会失败,并且需要生成所有标识值 - 您唯一的选择是OUTPUT子句。


db2*_*db2 9

除了性能之外,它们都有不同的含义。

SCOPE_IDENTITY()将为您提供直接插入当前范围内任何表的最后一个标识值(范围 = 批处理、存储过程等,但不在当前范围内触发的触发器内)。

IDENT_CURRENT()将为您提供任何用户从任何范围插入到特定表中的最后一个标识值。

@@IDENTITY为您提供由当前连接的最新 INSERT 语句生成的最后一个标识值,无论表或范围如何。(旁注:Access 使用此函数,因此在将值插入具有标识列的表中的触发器方面存在一些问题。)

使用MAX()TOP 1如果表具有否定的标识步骤,或者已插入行,则可能会给您完全错误的结果SET IDENTITY_INSERT。这是一个演示所有这些的脚本:

CREATE TABLE ReverseIdent (
    id int IDENTITY(9000,-1) NOT NULL PRIMARY KEY CLUSTERED,
    data char(4)
)

INSERT INTO ReverseIdent (data)
VALUES ('a'), ('b'), ('c')

SELECT * FROM ReverseIdent

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9000

SET IDENTITY_INSERT ReverseIdent ON

INSERT INTO ReverseIdent (id, data)
VALUES (9005, 'd')

SET IDENTITY_INSERT ReverseIdent OFF

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9005
Run Code Online (Sandbox Code Playgroud)

总结:坚持使用SCOPE_IDENTITY(), IDENT_CURRENT(), or @@IDENTITY,并确保您使用的是返回您实际需要的那个。

  • @AaronBertrand 我不确定我是否明白。最后生成的标识值是 8998(注意步长是 -1),这就是“IDENT_CURRENT()”返回的值。MAX() 永远不会返回第一行之外的正确值,因为 id 是向后计数的,并且在“IDENTITY_INSERT”打开的情况下,9005 不是*生成的*标识值,因此不会被“IDENT_CURRENT()”反映。但如果您确实想要“SCOPE_IDENTITY()”返回的结果,它*可能*会返回“不正确”的结果。选择适合工作的工具。 (2认同)