當需要確保資料行值唯一時,SQL Server 提供兩種機制:UNIQUE CONSTRAINT (唯一性約束) 與 UNIQUE INDEX (唯一索引)。兩者在查詢最佳化上效能相同,但在語意、標準相容性、管理工具呈現與變更彈性上有關鍵差異。本文透過實戰範例詳細比較,協助做出正確選擇。
| 面向 | UNIQUE CONSTRAINT | UNIQUE INDEX |
|---|---|---|
| 標準 | ANSI SQL 標準 | 非標準,廠商擴充 |
| 語意 | 邏輯資料模型一部分,表達業務規則 | 物理資料模型一部分,表達存取路徑 |
| 建立方式 | ALTER TABLE ... ADD CONSTRAINT | CREATE UNIQUE INDEX |
| 底層實作 | 自動建立唯一索引支援 | 直接建立索引 |
| 管理工具顯示 | SSMS「索引/鍵值」與「約束」雙重顯示 | 僅顯示於「索引」 |
| 變更彈性 | 需 DROP CONSTRAINT 再 ADD,或使用 DROP_EXISTING | 直接 DROP/CREATE 或 DROP_EXISTING |
CREATE TABLE dbo.CAR (
CarID INT IDENTITY(1,1) NOT NULL,
Make VARCHAR(10) NOT NULL,
Model VARCHAR(10) NOT NULL,
TotalDoors TINYINT NOT NULL,
TotalCylinders TINYINT NOT NULL,
CONSTRAINT PK_CAR PRIMARY KEY NONCLUSTERED (CarID),
CONSTRAINT UQ_CAR UNIQUE NONCLUSTERED (Make, Model)
);
GO
執行 sp_help CAR 可見:
UQ_CAR 存在於「約束」清單UQ_CAR 存在於「索引」清單嘗試直接 DROP INDEX 並 CREATE UNIQUE INDEX 包含 Year:
DROP INDEX dbo.CAR.UQ_CAR;
GO
CREATE UNIQUE INDEX UQ_CAR ON dbo.CAR (Make, Model, [Year]);
GO
報錯:無法卸除索引 'UQ_CAR',因為它是由約束 'UQ_CAR' 強制執行的。
ALTER TABLE dbo.CAR DROP CONSTRAINT UQ_CAR;
GO
ALTER TABLE dbo.CAR ADD CONSTRAINT UQ_CAR UNIQUE (Make, Model, [Year]);
GO
再次 sp_help CAR 驗證,約束與索引皆已更新包含 Year。
若所有報表皆依 Make, Model, Year 排序,將唯一性約束改為叢集索引可避免 Key Lookup:
CREATE UNIQUE CLUSTERED INDEX UQ_CAR ON dbo.CAR (Make, Model, [Year])
WITH DROP_EXISTING;
GO
關鍵點:DROP_EXISTING 允許在不變更索引鍵欄位順序/組成的前提下,直接轉換叢集/非叢集屬性。若需增減欄位,仍須走 DROP CONSTRAINT → ADD CONSTRAINT 流程。
CREATE UNIQUE INDEX ... WITH DROP_EXISTING 或重建約束。UQ_Table_Columns,索引建議 IX_Table_Columns,避免同名混淆。UNIQUE CONSTRAINT 與 UNIQUE INDEX 在查詢效能上等價,差異在於語意層級與變更管理。建議優先使用約束表達業務規則,僅在需要索引層級精細控制 (如 INCLUDE、WHERE 篩選、頻繁結構變更) 時改用索引。理解兩者轉換機制 (DROP_EXISTING、約束依賴),能避免線上變更時的阻塞與報錯。