ROWVERSION)INSERT/UPDATE 操作該資料列時,自動更新為當前 @@DBTS + 1VARBINARY(8))-- 建表:timestamp 欄位無需指定名稱 (系統自動命名為 timestamp/rowversion)
CREATE TABLE TestTable (
ID INT PRIMARY KEY,
DataCol VARCHAR(100),
Version ROWVERSION -- 或 TIMESTAMP,同義
);
-- 插入:Version 欄位給 NULL 或省略
INSERT INTO TestTable (ID, DataCol) VALUES (1, 'Initial');
INSERT INTO TestTable (ID, DataCol) VALUES (2, 'Second');
-- 查看:自動產生的二進位值
SELECT ID, DataCol, Version FROM TestTable;
-- 結果範例:
-- 1 | Initial | 0x00000000000007D0
-- 2 | Second | 0x00000000000007D1
-- 更新:Version 自動變更
UPDATE TestTable SET DataCol = 'Updated' WHERE ID = 1;
SELECT Version FROM TestTable WHERE ID = 1; -- 0x00000000000007D2 (新值)
SELECT @@DBTS; -- 回傳 VARBINARY(8),如 0x00000000000007D2
-- 等同於:
SELECT CONVERT(VARBINARY(8), @@DBTS);
TIMESTAMP/ROWVERSION 本質是 8 位元組無號整數,可安全轉換為 BIGINT (有號 8 位元組),數值相等。
-- Timestamp → BigInt
SELECT
Version,
CONVERT(BIGINT, Version) AS Version_BigInt,
CONVERT(VARBINARY(8), CONVERT(BIGINT, Version)) AS BackToVarBinary
FROM TestTable;
-- BigInt → Timestamp (比較/查詢用)
DECLARE @ts BIGINT = 2000; -- 對應 0x00000000000007D0
SELECT * FROM TestTable WHERE Version = CONVERT(VARBINARY(8), @ts);
-- 使用 @@DBTS 追蹤變更
DECLARE @last_check BIGINT = CONVERT(BIGINT, @@DBTS);
-- ... 一段時間後 ...
SELECT * FROM TestTable
WHERE CONVERT(BIGINT, Version) > @last_check; -- 找出此期間被修改的列
-- 應用讀取時取得 Version
SELECT ID, DataCol, Version FROM TestTable WHERE ID = 1;
-- 傳回 Version = 0x00000000000007D2
-- 更新時攜帶原始 Version 做 WHERE 條件
UPDATE TestTable
SET DataCol = 'New Value',
Version = Version -- 觸發自動更新,但這行其實多餘,WHERE 已足夠
WHERE ID = 1 AND Version = 0x00000000000007D2;
-- 檢查 @@ROWCOUNT
IF @@ROWCOUNT = 0
RAISERROR('資料已被其他使用者修改,請重新讀取', 16, 1);
優點:無鎖定、高並發、資料庫原生支援。
-- 記錄上次同步的 Timestamp
CREATE TABLE SyncWatermark (TableName SYSNAME PRIMARY KEY, LastVersion BIGINT);
INSERT INTO SyncWatermark VALUES ('TestTable', CONVERT(BIGINT, @@DBTS));
-- 定期同步:只抓取變更列
DECLARE @last BIGINT = (SELECT LastVersion FROM SyncWatermark WHERE TableName = 'TestTable');
SELECT ID, DataCol, Version
FROM TestTable
WHERE CONVERT(BIGINT, Version) > @last;
-- 更新水位線
UPDATE SyncWatermark SET LastVersion = CONVERT(BIGINT, @@DBTS) WHERE TableName = 'TestTable';
-- 結合多表的版本向量
SELECT
'Orders' AS TableName, MAX(CONVERT(BIGINT, Version)) AS MaxVersion FROM Orders
UNION ALL
SELECT 'OrderDetails', MAX(CONVERT(BIGINT, Version)) FROM OrderDetails
UNION ALL
SELECT 'Customers', MAX(CONVERT(BIGINT, Version)) FROM Customers;
FILLFACTOR = 100 最佳| 誤區 | 正確認知 |
|---|---|
| 以為包含日期時間資訊 | 純遞增數字,無時間語義,無法轉換為 DATETIME |
| 嘗試手動設定值 | INSERT/UPDATE 時給定值會報錯,只能給 NULL |
| 以為表級別唯一 | 資料庫級別唯一,跨表可比較大小 |
| 重啟資料庫會歸零 | 不歸零,僅還原備份/附加資料庫可能改變 |
| 轉 BIGINT 會溢位 | ROWVERSION 最大 2^64-1,BIGINT 最大 2^63-1,極極極罕見才溢位 (需 1.8×10^19 次更新),實務可忽略 |
| 用於分頁/排序替代 ID | 可用但不推薦:ID 語意清晰、可控;Version 為系統維護、間距不固定 |
BIGINT + INTERLOCKED.INCREMENT)、或使用 SYSTEM_VERSIONING (Temporal Tables)SET NOCOUNT ON;
-- 清理
IF OBJECT_ID('dbo.TimestampDemo') IS NOT NULL DROP TABLE dbo.TimestampDemo;
-- 建表
CREATE TABLE dbo.TimestampDemo (
ID INT IDENTITY PRIMARY KEY,
Info VARCHAR(50),
Ver ROWVERSION
);
-- 插入 3 列
INSERT INTO dbo.TimestampDemo (Info) VALUES ('Row 1'), ('Row 2'), ('Row 3');
-- 記錄初始版本
DECLARE @v1 VARBINARY(8), @v2 VARBINARY(8), @v3 VARBINARY(8);
SELECT @v1 = Ver FROM dbo.TimestampDemo WHERE ID = 1;
SELECT @v2 = Ver FROM dbo.TimestampDemo WHERE ID = 2;
SELECT @v3 = Ver FROM dbo.TimestampDemo WHERE ID = 3;
PRINT '初始版本: ID1=' + CONVERT(VARCHAR(20), @v1, 1) + ', ID2=' + CONVERT(VARCHAR(20), @v2, 1) + ', ID3=' + CONVERT(VARCHAR(20), @v3, 1);
PRINT 'BigInt: ID1=' + CAST(CONVERT(BIGINT, @v1) AS VARCHAR(20)) + ', ID2=' + CAST(CONVERT(BIGINT, @v2) AS VARCHAR(20)) + ', ID3=' + CAST(CONVERT(BIGINT, @v3) AS VARCHAR(20));
-- 更新 ID=2
UPDATE dbo.TimestampDemo SET Info = 'Row 2 Updated' WHERE ID = 2;
-- 查詢更新後版本
DECLARE @v2_new VARBINARY(8);
SELECT @v2_new = Ver FROM dbo.TimestampDemo WHERE ID = 2;
PRINT '更新後 ID2 版本: ' + CONVERT(VARCHAR(20), @v2_new, 1) + ' (BigInt: ' + CAST(CONVERT(BIGINT, @v2_new) AS VARCHAR(20)) + ')';
-- 驗證其他列版本未變
DECLARE @v1_chk VARBINARY(8), @v3_chk VARBINARY(8);
SELECT @v1_chk = Ver FROM dbo.TimestampDemo WHERE ID = 1;
SELECT @v3_chk = Ver FROM dbo.TimestampDemo WHERE ID = 3;
PRINT 'ID1 未變: ' + CASE WHEN @v1_chk = @v1 THEN 'True' ELSE 'False' END;
PRINT 'ID3 未變: ' + CASE WHEN @v3_chk = @v3 THEN 'True' ELSE 'False' END;
-- 使用 BigInt 範圍查詢找出變更列
DECLARE @threshold BIGINT = CONVERT(BIGINT, @v2); -- 更新前 ID2 的版本
SELECT ID, Info, Ver, CONVERT(BIGINT, Ver) AS Ver_BigInt
FROM dbo.TimestampDemo
WHERE CONVERT(BIGINT, Ver) > @threshold;
-- 清理
DROP TABLE dbo.TimestampDemo;
PRINT '測試完成。';
ROWVERSION/TIMESTAMP 是 SQL Server 實現無鎖樂觀並發、高效變更追蹤的核心工具。關鍵要點:
BIGINT 雙向無損轉換,便於應用層比較/儲存@@DBTS 實現資料庫/表級變更水位線追蹤善用此類型,能大幅簡化並發控制邏輯,避免悲觀鎖定帶來的封鎖與死結風險。