首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server @@IDENTITY 陷阱與 SCOPE_IDENTITY() 正確用法深度解析

     編輯:關於SqlServer

在觸發器、巢狀預存程序等多層級插入場景下,@@IDENTITY 會返回「最後一次跨範圍插入」的識別值,導致業務邏輯錯誤。本文透過實測對比,徹底釐清 @@IDENTITY、SCOPE_IDENTITY()、IDENT_CURRENT() 三者差異與適用場景。

問題重現:觸發器導致 @@IDENTITY 返回錯誤值

-- 建立測試表
CREATE TABLE T_A (A_ID INT IDENTITY(1,1) PRIMARY KEY, A_Name VARCHAR(20));
CREATE TABLE T_B (B_ID INT IDENTITY(100,1) PRIMARY KEY, B_Name VARCHAR(20));
GO

-- 在 T_A 建立 INSERT 觸發器,同步寫入 T_B
CREATE TRIGGER trg_T_A_Ins ON T_A AFTER INSERT AS
BEGIN
    INSERT INTO T_B (B_Name) SELECT 'From Trigger' FROM inserted;
END;
GO

-- 測試
INSERT INTO T_A (A_Name) VALUES ('Direct Insert');
SELECT @@IDENTITY AS AtAtIdentity, SCOPE_IDENTITY() AS ScopeIdentity;
-- 結果:@@IDENTITY = 100 (T_B 的值) | SCOPE_IDENTITY() = 1 (T_A 的值) ✅

三大函數對比總表

函數範圍會話隔離適用場景風險
@@IDENTITY全域 (跨 Trigger/SP/批次)當前會話極少數需追蹤跨範圍最後插入的審計場景觸發器/巢狀 SP 會汙染結果 ❌
SCOPE_IDENTITY()當前範圍 (Batch/SP/Trigger 同層級)當前會話**99% 情況的首選** ✅ 取得「我剛插入的那張表」自增值
IDENT_CURRENT('table')指定資料表 (跨會話、跨範圍)全域需知某表全域最後識別值 (如異步同步檢查)高併發下可能非當前會話所插 ❌

最佳實務建議

  1. 預存程序/批次插入後:永遠使用 SCOPE_IDENTITY()
  2. OUTPUT 子句 (SQL 2005+):更強大、集合導向,可一次取回多欄位
    INSERT INTO T_A (A_Name) OUTPUT INSERTED.A_ID VALUES ('Test');
  3. 避免在觸發器中依賴 @@IDENTITY:觸發器內若需父表 ID,應從 inserted 虛擬表取得
  4. 高併發環境:IDENT_CURRENT 僅供監控/診斷,不可用於業務邏輯取號

實測驗證腳本 (可直接執行)

USE tempdb;
GO
CREATE TABLE TZ (Z_id INT IDENTITY(1,1) PRIMARY KEY, Z_name VARCHAR(20) NOT NULL);
GO

-- 測試 1:單純插入
INSERT INTO TZ (Z_name) VALUES ('A');
SELECT @@IDENTITY AS [@@ID], SCOPE_IDENTITY() AS [SCOPE], IDENT_CURRENT('TZ') AS [CURRENT];
-- 三者皆為 1

-- 測試 2:加上觸發器
CREATE TRIGGER trg_TZ ON TZ AFTER INSERT AS
BEGIN
    DECLARE @t TABLE (id INT);
    INSERT INTO TZ (Z_name) OUTPUT INSERTED.Z_id INTO @t SELECT 'Trigger';
    -- 此處若用 @@IDENTITY 會被汙染
END;
GO
INSERT INTO TZ (Z_name) VALUES ('B');
SELECT @@IDENTITY, SCOPE_IDENTITY(), IDENT_CURRENT('TZ');
-- @@IDENTITY = 2 (觸發器插入) | SCOPE_IDENTITY() = 1 (主插入) ✅

牢記:SCOPE_IDENTITY() 是開發者的守護神,@@IDENTITY 是歷史包袱,IDENT_CURRENT() 是監控工具。選對函數,避開隱形 Bug。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved