postgresql 15 中条件触发器的 when 子句是执行前过滤的关键机制,必须定义在触发器中且仅支持 new/old 和字面量的布尔表达式,可避免70%以上无效调用;before 触发器中应直接赋值 new 字段而非执行 update。

PostgreSQL 15 中的条件触发器(即带 WHEN 子句的触发器)不是“可选优化项”,而是避免无效执行的第一道防线——它能直接砍掉 70% 以上的无意义触发调用,尤其在批量更新中效果立竿见影。
WHEN 子句必须写在触发器定义里,不能放在函数内部
很多人误以为在触发器函数里加 IF NEW.status != OLD.status THEN ... END IF; 就算“条件判断”,其实这毫无意义:函数仍会被每行调用一次,只是内部跳过逻辑。真正的条件过滤必须由数据库引擎在调用前完成。
-
WHEN必须紧跟在触发事件之后、FOR EACH ROW之前,语法位置固定 - 只支持布尔表达式,且不能引用函数(如
now())、子查询或跨表字段;只能用NEW、OLD和字面量 - 例如合法写法:
WHEN (NEW.amount != OLD.amount);非法写法:WHEN (SELECT is_active FROM users WHERE id = NEW.user_id) - PostgreSQL 15 对
WHEN表达式做了短路优化,简单比较几乎零开销
BEFORE 触发器 + WHEN + 直接赋值,替代 UPDATE 查询
想“自动修正字段”时,最常见错误是写 UPDATE orders SET updated_at = NOW() WHERE id = NEW.id —— 这会触发递归、锁表、且违反单表限制。正确做法是用 BEFORE 触发器直接改 NEW 值。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
-
BEFORE UPDATE触发器中,修改NEW.updated_at := NOW();即可,无需 SQL 语句 - 配合
WHEN (OLD.updated_at IS NULL OR OLD.updated_at 避免频繁重写 - 所有字段修正都走这条路,禁止在触发器里发任何 DML 到当前表
- 记得最后
RETURN NEW;,否则修改丢失(PostgreSQL 15 仍要求显式返回)
语句级触发器 + TRANSITION TABLES 替代大批量行级触发
当你要对一次 UPDATE 影响的 5000 行做聚合统计(比如更新后发通知),用 FOR EACH ROW 是灾难。PostgreSQL 15 完全支持 FOR EACH STATEMENT + OLD TABLE/NEW TABLE。
- 声明过渡表名必须用标识符,如
REFERENCING OLD TABLE AS old_rows NEW TABLE AS new_rows - 触发器函数内可用
SELECT count(*) FROM new_rows或INSERT INTO log SELECT * FROM new_rows - 过渡表自动带索引(主键或唯一约束列),但若需按非键字段
JOIN,得提前在原表建对应索引 - 注意:过渡表只在触发器函数生命周期内存在,不能被其他会话访问
EXPLAIN ANALYZE 看清触发器内查询的真实开销
别信“这个查询很简单”。触发器里的 SELECT balance FROM users WHERE id = NEW.user_id LIMIT 1 在高并发下可能变成热点锁源——因为每次都是独立的一致性读快照。
- 单独运行
EXPLAIN ANALYZE SELECT balance FROM users WHERE id = 123;,确认rows≤ 1,Index Scan而非Seq Scan - 如果表没主键或
id列没索引,加CREATE INDEX CONCURRENTLY ON users(id); - 避免
SELECT ... INTO,改用SELECT ... INTO STRICT或直接赋值:balance_var := (SELECT balance FROM users WHERE id = NEW.user_id LIMIT 1); - 哪怕只查一行,若该行正被其他事务更新,也会卡住触发器直到锁释放
真正难的不是写触发器,而是判断“这事该不该放触发器里做”。只要涉及跨表状态校验、IO、网络调用或复杂计算,一律移出——PostgreSQL 15 的触发器不是业务逻辑容器,它是原子性与一致性的守门人,不是万能胶水。










