SCOPE_IDENTITY() 返回当前作用域中最后插入的标识列值;它只返回同一作用域(如存储过程、批处理)内INSERT生成的ID,不受触发器或跨会话影响,若此前无INSERT或表无标识列则返回NULL。

SCOPE_IDENTITY() 返回什么值?
它只返回当前作用域(比如当前存储过程、当前批处理)中最后一条 INSERT 语句生成的自增列值。和 @@IDENTITY 不同,它不会被触发器里产生的新 ID 干扰;和 IDENT_CURRENT('table') 也不同,它不跨作用域、不跨会话。
为什么不能用 @@IDENTITY?
当表上有触发器,且触发器内部又执行了带自增列的 INSERT,@@IDENTITY 就会返回触发器里的 ID,而不是你主语句插入的 ID。这是最常见的误用场景。
- 主表
orders插入一行 → 生成 ID = 1001 - 触发器往
audit_log插入日志 → 生成 ID = 5001 - 此时
SELECT @@IDENTITY返回5001,不是你要的1001 -
SELECT SCOPE_IDENTITY()正确返回1001
在存储过程中怎么安全获取刚插入的 ID?
必须紧接在 INSERT 语句之后调用,中间不能有其他影响作用域的操作(如另一条 INSERT、显式事务控制语句 COMMIT 或 ROLLBACK)。
CREATE PROCEDURE InsertOrder
@CustomerID INT,
@TotalAmount DECIMAL(10,2)
AS
BEGIN
INSERT INTO orders (CustomerID, TotalAmount)
VALUES (@CustomerID, @TotalAmount);
<pre class="brush:php;toolbar:false;">-- ✅ 紧接着获取,且在同一作用域
SELECT SCOPE_IDENTITY() AS NewOrderID;END
- 不要写成两段独立批处理(比如用
GO分隔),否则作用域断开 - 避免在
INSERT后加UPDATE或DELETE,除非确认不影响自增列上下文 - 如果需要把 ID 赋给变量,用
DECLARE @NewID INT; SET @NewID = SCOPE_IDENTITY();
返回值类型和 NULL 风险要注意什么?
SCOPE_IDENTITY() 返回 numeric(38,0),不是 INT。如果目标列是 INT,直接赋值可能隐式转换失败(尤其在严格模式下)。更关键的是:如果前面没发生过 INSERT,或插入的表没有自增列,它返回 NULL。
- 务必检查是否为
NULL:IF @NewID IS NULL RAISERROR('Insert failed', 16, 1); - 赋值时建议显式转换:
SET @NewID = CAST(SCOPE_IDENTITY() AS INT); - 如果插入的是空表且未启用
IDENTITY_INSERT,也可能返回NULL,需结合业务逻辑判断
实际用的时候,最常出问题的不是函数本身,而是把它放在了错误的位置,或者忽略了 NULL 的可能性。










