哪一个是获取我刚刚通过插入生成的标识值的最佳选择?这些陈述对绩效有何影响?
SCOPE_IDENTITY()
- 汇总功能
MAX()
TOP 1
从TableName中选择IdentityColumnORDER BY IdentityColumn DESC
哪一个是获取我刚刚通过插入生成的标识值的最佳选择?这些陈述对绩效有何影响?
SCOPE_IDENTITY()
MAX()
TOP 1
从TableName中选择IdentityColumnORDER BY IdentityColumn DESC
Answers:
使用SCOPE_IDENTITY()
如果您插入单行并希望检索生成的ID。
CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1));
INSERT #a(x) VALUES('a');
SELECT SCOPE_IDENTITY();
结果:
----
1
OUTPUT
如果要插入多行并且需要检索生成的ID 集,请使用该子句。
INSERT #a(x)
OUTPUT inserted.identity_column
VALUES('b'),('c');
结果:
----
2
3
为什么这是最好的更快的选择?
除了性能,这些是在默认隔离级别和/或有多个用户的情况下唯一可以保证正确的方法。即使您忽略了正确性方面,SQL Server SCOPE_IDENTITY()
也会将插入的值保存在内存中,因此自然地,这比对表或对系统表进行单独的查询要快。
无视正确性就像是告诉邮递员他很好地完成了今天的邮件-他完成路线的速度比平均时间快10分钟,问题是,没有邮件被传递到正确的地方。
请勿使用以下任何一种:
@@IDENTITY
-由于不能在所有情况下都使用它,例如,当带有标识列的表具有触发器,并且该触发器也插入具有自己标识列的另一个表时,您将获得错误的值。IDENT_CURRENT()
-我在这里对此进行了详细介绍,评论也很有用,但是从本质上讲,在并发的情况下,您常常会得到错误的答案。MAX()
或TOP 1
-您必须使用可序列化的隔离保护这两个语句,以确保MAX()
您得到的不是别人的。这比仅使用昂贵得多SCOPE_IDENTITY()
。每当您插入两行或更多行,并且需要生成所有标识值时,这些功能也会失败-唯一的选择是使用OUTPUT
子句。
除了性能,它们都有不同的含义。
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
摘要:坚持使用SCOPE_IDENTITY()
,IDENT_CURRENT()
或@@IDENTITY
,并确保您使用的是可以返回实际需要的内容。
IDENT_CURRENT()
和@@IDENTITY
当你自己的脚本演示了他们的输出不正确的结果?
IDENT_CURRENT()
返回的值。MAX()永远不会返回超出第一行的正确值,因为id会向后计数,并且IDENTITY_INSERT
on表示9005不是生成的标识值,因此不会被反映IDENT_CURRENT()
。但是,如果您真正追求的是返回结果,它可能会返回“不正确”的结果SCOPE_IDENTITY()
。选择适合该工作的工具。
@@IDENTITY
几乎从来不是获取生成的身份值的理想方法。要点是,MAX()
或者说TOP 1
像是不太可靠的IDENT_CURRENT()
,如果您了解它的功能,则可以使用它。可能对维护工作或其他有用。