CHECK 約束是資料完整性的最後一道防線,能在資料寫入時強制驗證欄位值是否符合業務規則。本文系統涵蓋 語法建立、ALTER TABLE 管理、啟用/停用、效能優化原理、分區視圖應用、中繼資料查詢,並釐清 CHECK 約束與驗證觸發器的差異。
Msg 547, Level 16,交易回滾。CREATE TABLE table_name (
column1 datatype NULL/NOT NULL,
column2 datatype NULL/NOT NULL,
CONSTRAINT constraint_name CHECK (column_name condition) [DISABLE]
);
DISABLE 關鍵字:建立時暫不生效,後續需 ENABLE。
CREATE TABLE tb_supplier (
supplier_id INT,
supplier_name VARCHAR(50),
contact_name VARCHAR(60),
CONSTRAINT chk_supplier_id CHECK (supplier_id BETWEEN 100 AND 9999)
);
驗證:
INSERT INTO tb_supplier VALUES (200, 'dlt', 'stk'); -- 成功 INSERT INTO tb_supplier VALUES (1, 'david', 'stk'); -- 失敗:Msg 547
CREATE TABLE tb_products (
product_id INT NOT NULL,
product_name VARCHAR(100) NOT NULL,
supplier_id INT NOT NULL,
CONSTRAINT chk_product_name_upper CHECK (product_name = UPPER(product_name))
);
驗證: INSERT ... VALUES (2, 'LENOVO', 2) 成功;VALUES (1, 'iPhone', 1) 失敗。
ALTER TABLE table_name ADD CONSTRAINT constraint_name CHECK (column_name condition) [DISABLE];
範例: 限制供應商名稱僅能為 'IBM'、'LENOVO'、'Microsoft'
ALTER TABLE tb_supplier
ADD CONSTRAINT chk_supplier_name CHECK (supplier_name IN ('IBM','LENOVO','Microsoft'));
| 操作 | 語法 | 說明 |
|---|---|---|
| 啟用 | ALTER TABLE tb_supplier ENABLE CONSTRAINT chk_supplier_id; | 立即生效,現有資料若違規會報錯 |
| 停用 | ALTER TABLE tb_supplier DISABLE CONSTRAINT chk_supplier_id; | 暫停驗證,大量匯入資料時可提升速度 |
| 建立時停用 | CONSTRAINT ... CHECK (...) DISABLE | 同上,後續手動 ENABLE |
多數開發者僅視 CHECK 為資料驗證工具,實則查詢最佳化器會利用約束資訊簡化執行計劃。
SELECT * FROM tb_supplier WHERE supplier_id = 50; -- 約束為 BETWEEN 100 AND 9999
最佳化器識別 50 不滿足約束,直接回傳空結果,**完全略過表掃描**。

建立年度分區表 + CHECK 約束限制日期範圍:
CREATE TABLE Test2007 (ProductReviewID INT IDENTITY, ReviewDate DATETIME NOT NULL); ALTER TABLE Test2007 WITH CHECK ADD CONSTRAINT CK_Test2007 CHECK (ReviewDate >= '2007-01-01' AND ReviewDate < '2008-01-01'); CREATE TABLE Test2008 (ProductReviewID INT IDENTITY, ReviewDate DATETIME NOT NULL); ALTER TABLE Test2008 WITH CHECK ADD CONSTRAINT CK_Test2008 CHECK (ReviewDate >= '2008-01-01' AND ReviewDate < '2009-01-01'); CREATE VIEW testPartitionView AS SELECT * FROM Test2007 UNION ALL SELECT * FROM Test2008;
查詢不同年度:
SELECT * FROM testPartitionView WHERE ReviewDate = '2007-01-01'; -- 僅掃描 Test2007 SELECT * FROM testPartitionView WHERE ReviewDate = '2008-01-01'; -- 僅掃描 Test2008 SELECT * FROM testPartitionView WHERE ReviewDate = '2010-01-01'; -- 常數掃描,零 IO

結論: 當 CHECK 約束為單欄位、無函數、簡單範圍時,最佳化器能精準推導,實現分區剪枝與常數摺疊,大幅提升查詢效能。
SELECT
constraint_name, constraint_type, table_name,
search_condition, status
FROM user_constraints -- 或 all_constraints / dba_constraints
WHERE constraint_name = 'CHK_SUPPLIER_ID';
ALTER TABLE tb_supplier DROP CONSTRAINT chk_supplier_id;
CHK_表名_欄位_規則 格式 (如 CHK_Order_Amount_Positive)。DISABLE 約束,載入後 ENABLE 並驗證現有資料 (WITH CHECK)。善用 CHECK 約束,不僅能守護資料品質,更能讓查詢最佳化器「讀懂」您的資料分布,產出更優執行計劃。