多维聚合表维护必须严格按维度建主键、分操作类型处理、显式防null,并额外应对truncate和历史数据初始化;漏任一环节,统计值就会错或丢。

触发器能维护多维聚合表,但必须严格按维度建主键、分操作类型处理、显式防 NULL,并额外应对 TRUNCATE 和历史数据——漏掉任一环节,统计值就会错或丢。
多维聚合表的主键必须覆盖全部统计维度
比如按 sale_date、product_category、region 三字段聚合,汇总表 daily_sales_summary 的主键就得是这三列组合。否则:
- 缺失某维度会导致多条记录映射到同一逻辑口径(如不同 region 写进同一
sale_date行),更新时相互覆盖 - 主键不唯一会让
INSERT ON CONFLICT DO UPDATE找不到冲突目标,静默插入重复行 - 用
WHERE条件更新却没建对应索引,高并发下容易锁等待甚至死锁
AFTER INSERT/UPDATE/DELETE 必须分开写,不能混在一个触发器里
同一张源表的三类操作对聚合值的影响方向不同,合并在一个触发器里极易漏判或重复计算:
-
AFTER INSERT:只加新值,但得先判断NEW.amount IS NOT NULL,否则total = total + NULL会让整行变 NULL -
AFTER UPDATE:必须同时处理OLD和NEW,例如先SET total = total - OLD.amount,再SET total = total + NEW.amount,且两个判断都要防 NULL -
AFTER DELETE:只减旧值,同样要IF OLD.amount IS NOT NULL,否则减了个 NULL,结果不可控
TRUNCATE 不触发任何 DML 触发器,必须另配 EVENT TRIGGER 或禁用
这是 PostgreSQL 的确定行为,不是 bug。如果业务中存在 TRUNCATE sales 操作:
- 普通
AFTER DELETE触发器完全不执行,汇总表残留旧数据 要么建 - 更稳妥的做法是收回
TRUNCATE权限,在文档中明确要求“清空请用DELETE FROM sales”,确保走触发器路径
EVENT TRIGGER 监听 sql_drop 事件,手动清空或重建汇总表新建汇总表后必须手动初始化,触发器不会倒推历史数据
刚建好 daily_sales_summary 时它是空的,哪怕源表 sales 已有两年数据,触发器也无动于衷:
- 必须立刻执行一次初始化语句:
INSERT INTO daily_sales_summary SELECT sale_date, product_category, region, SUM(amount) FROM sales GROUP BY sale_date, product_category, region ON CONFLICT (sale_date, product_category, region) DO UPDATE SET total = EXCLUDED.total - 这条语句要加
ON CONFLICT,防止并发建表+初始化时重复插入失败 - 初始化完成后,才真正进入“实时维护”状态;之前所有变更都不会被补上
最常被忽略的是 NULL 处理和 TRUNCATE 响应——它们不出错,但会让统计值在沉默中持续偏离,直到某次对账才发现差了几万单。











