傳統設計以業務邏輯鍵 (單據號、單據號+商品碼、多欄位聯合鍵) 作主鍵,導致:
為每張表新增一個與業務無關的數值型代理鍵 (Surrogate Key) 作為主鍵;業務唯一性改由 UNIQUE 約束/索引 保證。
CREATE TABLE T_PK_DEMO (
U_ID BIGINT NOT NULL, -- 代理鍵
COL_OTHER VARCHAR(20) NOT NULL, -- 業務欄位
CONSTRAINT PK_T_PK_DEMO PRIMARY KEY NONCLUSTERED (U_ID)
);
SCOPE_IDENTITY() / OUTPUT 子句)。CREATE TABLE T_DB_SEQ (
SEQ_NAME VARCHAR(50) NOT NULL,
SEQ_OWNER VARCHAR(50) NOT NULL DEFAULT 'DBO',
SEQ_CURRENT BIGINT NOT NULL DEFAULT 0,
SEQ_MIN BIGINT NOT NULL DEFAULT 0,
SEQ_MAX BIGINT NOT NULL DEFAULT 9223372036854775807,
SEQ_STEP INT NOT NULL DEFAULT 1,
IF_CYCLE INT NOT NULL DEFAULT 0, -- 0=不循環, 1=循環
CONSTRAINT PK_T_DB_SEQ PRIMARY KEY CLUSTERED (SEQ_NAME, SEQ_OWNER)
);
F_create_SEQ(@seq_name):建立序列 (寫入 T_DB_SEQ)。F_GET_NEXT_SEQ_VAL(@seq_name):取得下一值 (含並發鎖定保護)。F_GET_SEQ_CUR_VAL、F_SET_SEQ_VAL、F_DEL_SEQ、F_SEQ_EXISTS 等輔助函數。-- 父表
CREATE TABLE T_PK_DEMO (
U_ID BIGINT NOT NULL PRIMARY KEY NONCLUSTERED,
COL_OTHER VARCHAR(20) NOT NULL
);
-- 子表:自身代理鍵 PK + 父表代理鍵 FK
CREATE TABLE T_PK_DEMO_C (
U_ID BIGINT NOT NULL PRIMARY KEY NONCLUSTERED,
COL_OTHER VARCHAR(20) NOT NULL,
P_ID BIGINT NOT NULL,
CONSTRAINT FK_T_PK_DEMO_C FOREIGN KEY (P_ID)
REFERENCES T_PK_DEMO(U_ID) ON DELETE CASCADE
);
代理鍵不承擔業務語義,需額外加約束:
-- 方式 1:資料庫層 UNIQUE 約束 (推薦)
ALTER TABLE T_PK_DEMO ADD CONSTRAINT UQ_T_PK_DEMO_ColOther UNIQUE NONCLUSTERED (COL_OTHER);
-- 方式 2:唯一非叢集索引
CREATE UNIQUE INDEX IX_T_PK_DEMO_ColOther ON T_PK_DEMO (COL_OTHER);
-- 方式 3:應用層檢查 (不推薦為唯一手段,易有競態條件)
OrderDate、CreatedTime)。採用代理鍵 + 叢集索引置於時間欄位,分頁查詢可直接利用叢集索引有序特性:
-- 高效分頁 (SQL 2012+ OFFSET/FETCH 或 CTE ROW_NUMBER)
SELECT * FROM Orders
WHERE CreatedTime >= @StartDate
ORDER BY CreatedTime DESC
OFFSET @PageSize * (@PageNo-1) ROWS FETCH NEXT @PageSize ROWS ONLY;
避免了傳統「雙重排序 + NOT IN」或「游標」的低效寫法。
代理鍵非銀彈,但能將「業務識別」與「關聯效能」解耦,是大型系統主鍵設計的工業標準做法。