TG_OP是PostgreSQL触发器中标识操作类型的内置变量,值恒为'INSERT'、'UPDATE'、'DELETE'或'TRUNCATE',由系统自动注入,比依赖NEW/OLD更直接可靠。

TG_OP 变量就是干这个的——它在触发器函数里直接告诉你当前操作类型
PostgreSQL 触发器函数中,TG_OP 是一个内置的字符串变量,值为 'INSERT'、'UPDATE'、'DELETE' 或 'TRUNCATE'。它不是靠查表或拼逻辑推出来的,是 PostgreSQL 在调用函数时自动注入的上下文信息,最直接、最可靠。
为什么不能用 NEW/OLD 字段组合来判断?
有人试图通过 NEW IS NOT NULL AND OLD IS NULL 判定 INSERT,NEW IS NOT NULL AND OLD IS NOT NULL 判定 UPDATE,这在多数情况下看似可行,但有硬伤:
-
BEFORE INSERT和AFTER INSERT中,OLD永远为NULL,但NEW也只在行级触发器中可用;语句级触发器里连NEW都没有 -
INSTEAD OF触发器(常用于视图)中,OLD和NEW的存在性与 DML 类型不严格一一对应 - 如果触发器定义为
FOR EACH STATEMENT,NEW和OLD根本不可用,此时唯一能用的只有TG_OP
如何在 PL/pgSQL 函数里安全使用 TG_OP?
直接用 CASE 或 IF 判断 TG_OP 值即可,注意大小写敏感,且值恒为全大写字符串:
CREATE OR REPLACE FUNCTION log_operation()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO audit_log (action, table_name, row_id, created_at)
VALUES ('INSERT', TG_TABLE_NAME, NEW.id, NOW());
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO audit_log (action, table_name, row_id, old_data, new_data, created_at)
VALUES ('UPDATE', TG_TABLE_NAME, OLD.id,
ROW(OLD.*), ROW(NEW.*), NOW());
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO audit_log (action, table_name, row_id, old_data, created_at)
VALUES ('DELETE', TG_TABLE_NAME, OLD.id, ROW(OLD.*), NOW());
END IF;
RETURN NEW; -- 对于 BEFORE 触发器,可修改 NEW 并返回;AFTER 中通常 RETURN NULL 或 NEW(无实质影响)
END;
$$ LANGUAGE plpgsql;关键点:
-
TG_OP在函数体任何位置都可读,无需声明 - 不要对
TG_OP做模糊匹配(如LIKE 'INS%'),它永远是精确的四个值之一 - 如果触发器只响应一种操作(比如只绑了
UPDATE),TG_OP仍会是'UPDATE',但写函数时别省略判断——否则迁移或复用时容易出错
TG_OP 在 BEFORE/AFTER 中的行为一致吗?
一致。无论 BEFORE 还是 AFTER,TG_OP 的值都反映原始 SQL 语句的操作意图,不会因触发时机改变。区别只在于:
-
BEFORE中你可以修改NEW(INSERT/UPDATE)或拒绝操作(RETURN NULL) -
AFTER中NEW和OLD已落盘,不能再改数据,但能确保操作已成功执行 - 两者中
TG_OP的值完全相同,且是唯一权威来源
真正容易被忽略的是:TG_OP 是触发器函数的“第一手事实”,不是推论结果。只要触发器定义没写错,它就绝不会撒谎——而所有基于 NEW/OLD 的判断,都隐含了对触发器类型(行级/语句级)、时机(BEFORE/AFTER)、对象(表/视图)的假设,一不留神就掉坑里。

















