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()
您要插入单行并想要检索生成的 ID,请使用此选项。结果:
OUTPUT
如果要插入多行并且需要检索生成的 ID集,请使用该子句。结果:
撇开性能不谈,这些是唯一保证在默认隔离级别和/或多个用户下正确的。即使您忽略正确性方面,SQL Server 也会将插入的值保存
SCOPE_IDENTITY()
在内存中,因此这自然会比针对表或系统表运行您自己的独立查询要快。忽略正确性方面就像告诉邮递员他今天的邮件做得很好 - 他完成了他的路线比他的平均时间快了 10 分钟,问题是,没有一封邮件被送到正确的房子。
不要使用以下任何一种:
@@IDENTITY
- 因为这不能在所有场景中使用,例如当一个带有标识列的表有一个触发器,该触发器也插入到另一个具有自己的标识列的表中时——你会得到错误的值。IDENT_CURRENT()
- 我在这里详细介绍了这一点,评论也是有用的阅读,但本质上,在并发下,你经常会得到错误的答案。MAX()
或者TOP 1
- 你必须用可序列化的隔离来保护这两个语句,以确保MAX()
你得到的不是别人的。这比仅使用SCOPE_IDENTITY()
.每当您插入两行或更多行并且需要生成所有标识值时,这些函数也会失败 - 您唯一的选择是
OUTPUT
子句。除了性能之外,它们都有相当不同的含义。
SCOPE_IDENTITY()
将为您提供在当前范围内直接插入任何表的最后一个标识值(范围 = 批处理、存储过程等,但不在当前范围内触发的触发器内)。IDENT_CURRENT()
将为您提供任何用户从任何范围插入特定表的最后一个标识值。@@IDENTITY
为您提供由当前连接的最新 INSERT 语句生成的最后一个标识值,无论表或范围如何。(旁注:Access 使用此函数,因此在将值插入具有标识列的表中的触发器存在一些问题。)如果表有一个否定的标识步骤,或者在播放中插入了行,使用
MAX()
orTOP 1
会给你完全错误的结果。SET IDENTITY_INSERT
这是一个演示所有这些的脚本:摘要:坚持使用
SCOPE_IDENTITY()
、IDENT_CURRENT()
或@@IDENTITY
,并确保您使用的是返回您实际需要的那个。