需求:表 A 的主鍵為 (ProductID, RetailerID, Date),其中 Date 僅保留「年-月-日」,不含時分秒。應用層若直接寫入 DATETIME 完整時間,會導致主鍵衝突或語意不符。若無法控制應用端輸入,必須在資料庫層透過觸發器強制截取日期。
NEW、OLD 伪表,可直接賦值 NEW.Date = TRUNC(NEW.Date),修改生效、無額外 I/O。NEW/OLD,僅提供 INSERTED、DELETED 邏輯表,無法在觸發器中直接修改即將寫入的值,只能事後 UPDATE 回寫。CREATE TRIGGER A_T ON A
FOR INSERT, UPDATE
AS
BEGIN
UPDATE A
SET Date = CONVERT(DATE, I.Date) -- SQL Server 2008+ 直接用 DATE 類型
FROM INSERTED I
WHERE I.ID = A.ID;
END;
副作用:每次 INSERT/UPDATE 會觸發兩次寫入操作(原操作 + 觸發器 UPDATE),輸出訊息顯示 (1 行受影響) 兩次,效能與日誌量雙倍增長。
| 方案 | 優點 | 缺點 | 適用版本 |
|---|---|---|---|
| AFTER 觸發器 UPDATE 回寫 | 相容舊版、邏輯簡單 | 雙倍寫入、觸發遞迴風險、需防呆 | 所有版本 |
| 改用 DATE 類型(SQL Server 2008+) | 儲存層面原生只存日期、無觸發器開銷 | 需修改表結構、應用層參數型別同步調整 | 2008+ |
| INSTEAD OF 觸發器 | 可攔截並重寫 INSERT/UPDATE 邏輯、單次寫入 | 需手寫完整欄位清單、維護成本高、易遺漏欄位 | 2000+ |
| 計算欄位 + 索引 | 不存冗余、查詢可用索引 | 無法直接做主鍵(除非 PERSISTED),主鍵仍需原始欄位 | 2005+ |
| CHECK 條件約束 | 攔截非法資料、零額外 I/O | 只能拒絕、無法自動修正(截取日期) | 所有版本 |
DATE 類型(SQL Server 2008+),從源頭解決,零執行期開銷。CHECK (Date = CONVERT(DATE, Date)) 條件約束,強制應用端自行修正。