要在 PostgreSQL 建立一個觸發器,你要定義這幾個部分:
- 觸發器的名字。
- 事件類型(
INSERT、UPDATE、DELETE)。 - 執行時機(
BEFORE或AFTER)。 - 它所屬的資料表。
- 要執行的 function(可以用 PL/pgSQL 或其他語言寫)。
這是指令的大致結構:
CREATE TRIGGER 觸發器名稱
[BEFORE | AFTER] {INSERT | UPDATE | DELETE}
ON 資料表名稱
[FOR EACH ROW | FOR EACH STATEMENT]
WHEN (條件)
EXECUTE FUNCTION 函數名稱();
簡單觸發器範例
我們來建立一個基本的 students 資料表,然後加上一個在新增資料後會觸發的觸發器。
先建立 students 資料表
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
這裡發生了什麼? 我們建立了一個有 id、name 跟 last_modified 欄位的資料表。last_modified 這個欄位會存最後一次修改的日期時間。
觸發器一定要綁 function。先來寫一個簡單的 function,每次新增資料時會更新 last_modified 欄位:
CREATE OR REPLACE FUNCTION update_last_modified()
RETURNS TRIGGER AS $$
BEGIN
-- 設定現在的日期時間到 last_modified 欄位
NEW.last_modified := CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
這是什麼魔法?
NEW— 這是個特別的變數,會存新資料列的值(用在INSERT或UPDATE事件)。CURRENT_TIMESTAMP— 這個 function 會回傳現在的日期時間。RETURN NEW— 回傳修改過的資料列,讓它可以被存下來。
現在來建立觸發器本體:
CREATE TRIGGER set_last_modified
AFTER INSERT
ON students
FOR EACH ROW
EXECUTE FUNCTION update_last_modified();
解釋一下:
AFTER INSERT:這個觸發器會在新增資料後執行。ON students:觸發器是綁在students這個表。FOR EACH ROW:每一筆新資料都會觸發一次。EXECUTE FUNCTION:指定要執行哪個 function。
測試觸發器
來看看我們的觸發器有沒有動:
INSERT INTO students (name) VALUES ('Alice');
SELECT * FROM students;
你會看到類似這樣的結果:
| id | name | last_modified |
|---|---|---|
| 1 | Alice | 2023-10-15 14:23:45 |
觸發器自動幫你更新了 last_modified 欄位。魔法嗎?不是啦,這就是 PostgreSQL。
用 WHEN 加條件
有時候你不想每次都執行觸發器,只想在特定情況下才動。這時就要用 WHEN 關鍵字。
來看個例子,只有特定值才會觸發。
假設我們只想讓名字是 "Alice" 的學生才觸發。來改一下觸發器:
CREATE OR REPLACE FUNCTION update_last_modified_condition()
RETURNS TRIGGER AS $$
BEGIN
NEW.last_modified := CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER set_last_modified_condition
AFTER INSERT
ON students
FOR EACH ROW
WHEN (NEW.name = 'Alice')
EXECUTE FUNCTION update_last_modified_condition();
現在這個觸發器只會幫名字是 "Alice" 的學生更新 last_modified。
來測試一下:
INSERT INTO students (name) VALUES ('Alice');
INSERT INTO students (name) VALUES ('Bob');
SELECT * FROM students;
結果:
| id | name | last_modified |
|---|---|---|
| 1 | Alice | 2023-10-15 14:30:00 |
| 2 | Bob | (NULL) |
注意: "Bob" 這個學生的 last_modified 是空的,因為觸發器沒動。
觸發器跟 function 的關係:EXECUTE FUNCTION
function 就是觸發器的核心。沒有 function,觸發器根本沒辦法運作。在 PostgreSQL 裡你可以用 PL/pgSQL 或其他支援的語言(像 Python、C)寫 function。
來個 PL/pgSQL function 的例子。
我們來寫一個 function,把變動記錄到另一個 audit_log 表。
先建立 audit_log 表
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
operation TEXT NOT NULL,
student_id INTEGER NOT NULL,
log_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
然後寫 function:
CREATE OR REPLACE FUNCTION log_insert()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO audit_log (operation, student_id)
VALUES ('INSERT', NEW.id);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
再來寫個觸發器:
CREATE TRIGGER log_student_insert
AFTER INSERT
ON students
FOR EACH ROW
EXECUTE FUNCTION log_insert();
來測試一下:
INSERT INTO students (name) VALUES ('Charlie');
SELECT * FROM audit_log;
你會看到類似這樣的結果:
| id | operation | student_id | log_time |
|---|---|---|---|
| 1 | INSERT | 3 | 2023-10-15 14:35:00 |
觸發器自動幫你記錄了新增資料的 log。
觸發器常見錯誤跟注意事項
錯誤:沒有 function。 如果你想建立沒有 function 的觸發器,PostgreSQL 會直接報錯。一定要先寫好 function 再建觸發器。
效能問題。 如果你有一堆觸發器或 function 寫太複雜,資料庫會變慢。要小心用。
遞迴。 如果觸發器又去改同一個表,可能會造成無限迴圈。記得用 WHEN 條件來避免這種情況。
GO TO FULL VERSION