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

善用 CHECK 約束落實業務規則,拒絕濫用觸發器

     編輯:關於SqlServer

在資料庫設計中,CHECK 約束常被開發者忽視,許多本應由宣告式約束處理的業務規則,卻被硬塞進觸發器(Trigger)裡。這不僅增加代碼維護成本,更在效能與交易管理上埋下隱患。本文透過實戰案例,示範如何用 CHECK 約束優雅地實現單欄、跨欄、甚至條件式業務驗證。

為何首選 CHECK 約束?

  • 零代碼維護: 純宣告式語法,無需編寫 T-SQL 邏輯,降低 Bug 率。
  • 引擎層面攔截: 在資料寫入存儲引擎之前驗證,觸發器則在寫入之後檢查,若觸發回滾,交易成本更高(日誌、鎖持有時間更長)。
  • 效能優勢: 優化器可直接利用約束資訊簡化執行計畫;觸發器則是黑盒,難以優化。

案例一:單欄域完整性 — 薪資範圍限制

ALTER TABLE dbo.Employee
ADD CONSTRAINT CK_Employee_Salary
CHECK (Salary BETWEEN 30000 AND 100000);

嘗試插入 110,000 會直接被引擎拒絕,錯誤訊息清晰指向約束名稱,前端無需額外捕獲邏輯。

案例二:跨欄條件約束 — 獎金比例與職位綁定

業務規則: 主管獎金比例 ≥ 5%,非主管 < 5%。

錯誤嘗試:

CHECK (Is_Manager = 1 AND BonusPct >= 5.00)

此寫法強制所有員工必須是主管且獎金 ≥ 5%,違反業務意圖。

正確寫法 — 使用 CASE 表達式實現條件邏輯:

CHECK (
    CASE 
        WHEN Is_Manager <> 1 AND BonusPct >= 5.00 THEN 1  -- 非主管卻領高獎金 → 違規
        ELSE 0 
    END = 0
)

邏輯解讀:只有「非主管且獎金 ≥ 5%」才回傳 1(違規),其他情況回傳 0(合規)。

案例三:NULL 值陷阱與修正

若 Is_Manager 允許 NULL,上述約束會因 NULL <> 1 結果為 UNKNOWN,導致 CASE 走向 ELSE 0,讓 NULL 職位員工繞過檢查。

修正版:

CHECK (
    CASE 
        WHEN Is_Manager IS NULL AND BonusPct >= 5.00 THEN 1
        WHEN Is_Manager <> 1 AND BonusPct >= 5.00 THEN 1
        ELSE 0 
    END = 0
)

或從根源解決:ALTER TABLE ... ALTER COLUMN Is_Manager BIT NOT NULL DEFAULT 0。

進階警示:標量函數陷阱

有人嘗試在 CHECK 中呼叫標量函數掃描整張表(如統計總獎金上限),SQL Server MVP David Portas 指出:這會導致並發嚴重下降、鎖升級、甚至死鎖。**CHECK 約束只能驗證「當前資料列」的邏輯真偽,不可跨列、跨表運算。**

總結

CHECK 約束是資料庫引擎原生提供的「業務規則執行器」,適用於:

  • 單欄值域(範圍、列表、格式)
  • 同一資料列內多欄邏輯關係(含條件判斷)
  • 需極高效能、零代碼維護的場景

觸發器請留給:跨表驗證、稽核軌跡、複雜業務流程控制。工具用對地方,資料庫才能穩健演進。

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