CodeGym /課程 /SQL SELF /觸發器語法基礎:CREATE TRIGGER、WHEN、EXECUTE FUNCTION

觸發器語法基礎:CREATE TRIGGER、WHEN、EXECUTE FUNCTION

SQL SELF
等級 57, 課堂 2
開放

要在 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;

這是什麼魔法?

  1. NEW — 這是個特別的變數,會存新資料列的值(用在 INSERT 或 UPDATE 事件)。
  2. CURRENT_TIMESTAMP — 這個 function 會回傳現在的日期時間。
  3. 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 條件來避免這種情況。

2
任務
SQL SELF, 等級 57, 課堂 2
上鎖
建立簡單的觸發器來記錄日誌
建立簡單的觸發器來記錄日誌
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION