最常见写法是UPDATE父表并子查询求和,但应改用增量更新;INSERT/UPDATE/DELETE需分别建触发器;UPDATE触发器须加WHEN (OLD.child_value IS DISTINCT FROM NEW.child_value);函数中需处理父表记录不存在的情况。

触发器里怎么写 UPDATE 语句更新父表聚合值
直接在触发器函数中用 UPDATE parent_table SET total = (SELECT SUM(child_value) FROM child_table WHERE parent_id = NEW.parent_id) 是最常见写法,但要注意:如果子表数据量大,每次 INSERT/UPDATE 都全量扫描子表会拖慢性能;更稳妥的做法是增量更新——只加减变化值。比如 INSERT 新子记录时,用 UPDATE parent_table SET total = total + NEW.child_value WHERE id = NEW.parent_id。
INSERT/UPDATE/DELETE 触发器必须分开定义
PostgreSQL 不支持一个触发器响应多种操作类型(比如不能写 AFTER INSERT OR UPDATE OR DELETE 并在函数里判断 TG_OP 来分支处理),必须为每种操作单独建触发器。否则会报错 ERROR: trigger procedure cannot be called directly 或漏掉某类变更。实际部署时至少要三组:
CREATE TRIGGER child_insert_update_total AFTER INSERT ON child_table FOR EACH ROW EXECUTE FUNCTION update_parent_total()CREATE TRIGGER child_update_update_total AFTER UPDATE ON child_table FOR EACH ROW WHEN (OLD.child_value IS DISTINCT FROM NEW.child_value) EXECUTE FUNCTION update_parent_total()CREATE TRIGGER child_delete_update_total AFTER DELETE ON child_table FOR EACH ROW EXECUTE FUNCTION update_parent_total()
UPDATE 触发器里必须加 WHEN 条件避免无意义更新
如果子表某行只是更新了非聚合字段(比如备注、状态),但 child_value 没变,触发器仍会执行一次 UPDATE parent_table,造成无谓的行锁和 WAL 日志写入。必须用 WHEN (OLD.child_value IS DISTINCT FROM NEW.child_value) 过滤。注意不能用 !=,因为 NULL 比较会返回 NULL,导致条件不成立;IS DISTINCT FROM 才能正确处理 NULL。
触发器函数里别忘了处理父表记录不存在的情况
如果先插入子记录、后创建父记录,或者父表被手动删过,触发器里的 UPDATE 就不会生效,聚合值就丢了。稳妥做法是在触发器函数里补一句 INSERT ... ON CONFLICT (id) DO UPDATE SET total = EXCLUDED.total,或者更简单:先用 UPDATE,再用 GET DIAGNOSTICS rowcount = ROW_COUNT 判断是否更新成功,没更新就 INSERT。否则上线后某天发现总金额对不上,排查起来很费劲。

















