Trigger 觸發器
什麼是 Trigger?
比喻:Trigger 就像自動感應門 🚪
有人走近(事件發生)→ 門自動打開(觸發動作)。 當資料被新增/修改/刪除時,Trigger 會自動執行你預設的程式。
建立 Trigger(PostgreSQL)
-- Step 1:建立 Trigger 函數
CREATE OR REPLACE FUNCTION update_timestamp()
RETURNS TRIGGER -- ← 回傳類型必須是 TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
NEW.UpdatedAt = CURRENT_TIMESTAMP; -- ← NEW 代表即將寫入的新資料
RETURN NEW; -- ← 回傳修改後的資料
END;
$$;
-- Step 2:建立 Trigger,綁定到表
CREATE TRIGGER trg_students_updated -- ← Trigger 名稱
BEFORE UPDATE -- ← 在 UPDATE 之前觸發
ON Students -- ← 綁定到 Students 表
FOR EACH ROW -- ← 每一列都觸發
EXECUTE FUNCTION update_timestamp(); -- ← 執行的函數
逐行解析:
BEFORE UPDATE -- 時機:在 UPDATE 實際執行之前
ON Students -- 觸發表:Students
FOR EACH ROW -- 範圍:影響到的每一列都觸發一次
EXECUTE FUNCTION update_timestamp() -- 執行的動作
Trigger 的時機
| 時機 | 說明 |
|---|---|
BEFORE INSERT |
新增前(可修改 NEW 的值) |
AFTER INSERT |
新增後(適合寫日誌) |
BEFORE UPDATE |
修改前(可修改 NEW 的值) |
AFTER UPDATE |
修改後(適合同步、通知) |
BEFORE DELETE |
刪除前(可取消刪除) |
AFTER DELETE |
刪除後(適合清理關聯資料) |
NEW 和 OLD
CREATE OR REPLACE FUNCTION log_score_change()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
-- NEW = 更新後的新值
-- OLD = 更新前的舊值
INSERT INTO ScoreLog (StudentId, OldScore, NewScore, ChangedAt)
VALUES (
OLD.Id, -- ← 學生 Id(新舊都一樣)
OLD.Score, -- ← 舊分數
NEW.Score, -- ← 新分數
CURRENT_TIMESTAMP
);
RETURN NEW;
END;
$$;
CREATE TRIGGER trg_score_change
AFTER UPDATE OF Score -- ← 只在 Score 欄位被改時觸發
ON Students
FOR EACH ROW
WHEN (OLD.Score IS DISTINCT FROM NEW.Score) -- ← 值真的有變才觸發
EXECUTE FUNCTION log_score_change();
實用範例:軟刪除
-- 不真的刪除,而是標記為已刪除
CREATE OR REPLACE FUNCTION soft_delete()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
-- 攔截 DELETE,改成 UPDATE
UPDATE Students
SET IsDeleted = true, DeletedAt = CURRENT_TIMESTAMP
WHERE Id = OLD.Id;
RETURN NULL; -- ← 回傳 NULL = 取消原本的 DELETE
END;
$$;
CREATE TRIGGER trg_soft_delete
BEFORE DELETE ON Students
FOR EACH ROW
EXECUTE FUNCTION soft_delete();
管理 Trigger
-- 暫時停用 Trigger
ALTER TABLE Students DISABLE TRIGGER trg_students_updated;
-- 重新啟用
ALTER TABLE Students ENABLE TRIGGER trg_students_updated;
-- 刪除 Trigger
DROP TRIGGER trg_students_updated ON Students;
-- 查看表的所有 Trigger
SELECT trigger_name, event_manipulation, action_timing
FROM information_schema.triggers
WHERE event_object_table = 'students';
⚠️ Trigger 注意事項
- 效能影響:每次操作都會觸發,大量寫入時會變慢
- 難除錯:隱式執行,出問題不容易發現
- 避免無限迴圈:Trigger A 更新表 → 觸發 Trigger B → 又更新 → 觸發 Trigger A...
- 現代替代方案:在應用程式層處理(EF Core Interceptor、Middleware 等)