PostgreSQL 15中OLD/NEW伪记录严格按触发时机可用:INSERT仅NEW有效,DELETE仅OLD有效,UPDATE两者皆可;访问字段须大小写精确匹配表定义,比较需用IS DISTINCT FROM处理NULL,禁止直接=或!=比较RECORD。

PostgreSQL 15 中,OLD 和 NEW 是行级触发器函数内可用的伪记录(RECORD 类型),但**不能直接当普通变量用,也不能跨操作类型无条件访问**——误用会报错或逻辑失效。
触发器里怎么安全读取 OLD 和 NEW
PostgreSQL 的 OLD/NEW 可用性严格取决于触发时机和操作类型,不是“有就一定能读”:
-
BEFORE INSERT:只有NEW有效,OLD不存在(报错record "old" is not assigned) -
BEFORE UPDATE:OLD和NEW都可读,且可在函数中修改NEW字段(如自动设updated_at) -
BEFORE DELETE:只有OLD有效,NEW不可用(报错record "new" is not assigned) -
AFTER类触发器:OLD/NEW仍可读,但不能再改NEW(返回值必须是OLD或NEW,否则中断操作)
OLD.id 和 NEW.name 字段访问要注意什么
字段名区分大小写,且必须与表定义完全一致;常见踩坑点包括:
- 表字段是
user_name,却写成NEW.username→ 报错column "username" of relation does not exist - 字段类型为
jsonb或bytea,直接NEW.payload::text可能失败,需先检查是否为 NULL 或用pg_typeof(NEW.payload)调试 - 在
IF OLD != NEW THEN这类比较中,OLD和NEW是RECORD,不能直接用=或!=比较 —— 必须用ROW(OLD.*) IS DISTINCT FROM ROW(NEW.*) - 想判断单个字段是否变化,别用
NEW.status != OLD.status,NULL 值会导致漏判;改用NEW.status IS DISTINCT FROM OLD.status
如何在 AFTER 触发器中拿到刚插入的主键值
对 INSERT 操作,NEW 就是刚插入的整行数据,主键值直接取即可:
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
CREATE OR REPLACE FUNCTION log_insert() RETURNS TRIGGER AS $$ BEGIN RAISE NOTICE 'Inserted ID: %', NEW.id; -- 假设主键字段叫 id RETURN NEW; END; $$ LANGUAGE plpgsql;
注意:
- 如果主键是
SERIAL或IDENTITY,NEW.id在AFTER INSERT中一定有值;但在BEFORE INSERT中,若未显式赋值,NEW.id为 NULL - 若用
RETURN NULL(如软删场景),NEW不再可用,后续逻辑拿不到主键 - 多列主键时,需全部引用,如
NEW.order_id和NEW.line_no
为什么 row_to_json(NEW) 有时会失败
row_to_json() 看似方便,但实际容易出错:
- 表含
refcursor、大对象(LOB)、自定义域或某些扩展类型时,row_to_json(NEW)直接报错could not serialize value - 字段为
jsonb时,row_to_json()会把它转成字符串包裹的 JSON,而非原生结构(例如"{\"a\":1}"),导致下游解析困难 - 空值处理不一致:
row_to_json()保留 NULL 字段,而手动JSON_BUILD_OBJECT可控性更强 - 更稳妥的做法是显式构造,比如:
JSON_BUILD_OBJECT('id', NEW.id, 'name', NEW.name, 'status', COALESCE(NEW.status, 'unknown'))
真正麻烦的不是语法,而是字段变更时的隐式行为——比如加了个 NOT NULL 字段却忘了在触发器里初始化,NEW 赋值就会失败;或者用 IS DISTINCT FROM 时漏掉嵌套 JSONB 字段的深层差异。这些都得靠实测 + 日志 RAISE DEBUG 验证,不能只看文档。

















