scope_identity()是最安全的选择,因为它仅返回当前作用域和当前会话中最后插入的标识值,不受触发器或其他会话操作干扰,而@@identity可能返回触发器生成的id,ident_current()则跨会话不安全。

为什么 SCOPE_IDENTITY() 是最安全的选择
在 SQL Server 存储过程中插入新记录后,要拿到刚生成的自增 IDENTITY 值,SCOPE_IDENTITY() 是唯一能准确返回「当前作用域、当前会话」中最后插入 ID 的函数。它不会受触发器里其他插入操作干扰,也不跨会话污染,比 @@IDENTITY 和 IDENT_CURRENT() 更可靠。
SCOPE_IDENTITY() 必须紧跟 INSERT 之后调用
它的值只保留到当前语句批(batch)结束前,且依赖执行顺序。一旦中间穿插了其他语句(哪怕只是 SELECT 1 或变量赋值),就可能取到空或错误值。
常见错误写法:
INSERT INTO users (name) VALUES ('Alice');
DECLARE @id INT = SCOPE_IDENTITY(); -- ✅ 正确:紧接 INSERT
-- ... 其他逻辑
UPDATE logs SET user_id = @id WHERE ...; -- ✅ 可用
错误写法示例:
INSERT INTO users (name) VALUES ('Alice');
IF @@ROWCOUNT > 0
BEGIN
SELECT 'Inserted'; -- ❌ 这条 SELECT 会“污染” SCOPE_IDENTITY() 的上下文
DECLARE @id INT = SCOPE_IDENTITY(); -- ❌ 此时可能返回 NULL
END
- 必须在同一个批(batch)内,且是
INSERT后第一条可读取该值的语句 - 不能放在
IF、BEGIN...END等嵌套块内部再调用,除非确保 INSERT 和 SCOPE_IDENTITY() 在同一作用域层级 - 存储过程里推荐统一用
DECLARE @new_id INT = SCOPE_IDENTITY();一行完成赋值
和 @@IDENTITY、IDENT_CURRENT() 的关键区别
三者都返回 ID,但行为完全不同:
-
@@IDENTITY:返回当前会话中**最后生成的任何 IDENTITY 值**,包括触发器里插入的——这是最大陷阱,极易出错 -
IDENT_CURRENT('table_name'):返回指定表的最新 ID,**不区分会话也不限作用域**,并发下完全不可靠 -
SCOPE_IDENTITY():只认「当前作用域 + 当前会话」,INSERT 后立刻取,精准可控
例如:users 表有 INSERT 触发器往 audit_log 插入一条记录。此时 @@IDENTITY 返回的是 audit_log 的 ID,而 SCOPE_IDENTITY() 才是 users 的新 ID。
在多语句或 OUTPUT 子句场景下怎么选
如果 INSERT 本身带 OUTPUT,比如需要同时返回 ID 和其他计算字段,优先用 OUTPUT 而不是 SCOPE_IDENTITY():
INSERT INTO users (name, created_at)
OUTPUT INSERTED.id, INSERTED.name
VALUES ('Bob', GETDATE());
这时候 OUTPUT 更直接、更高效,还能一次返回多列;SCOPE_IDENTITY() 只适合简单单行插入后取 ID 的场景。
注意:OUTPUT 在某些 ORM 或客户端驱动中解析较复杂,若只需 ID 且逻辑简单,SCOPE_IDENTITY() 仍是首选。
真正容易被忽略的是:当存储过程里有多个 INSERT,或者用了临时表、表变量再插入时,SCOPE_IDENTITY() 只响应最近那个 INSERT——务必确认你盯住的是目标语句,而不是被中间某条日志插入悄悄覆盖了。










