MySQL需显式用JSON_OBJECT拼字段,PostgreSQL可用row_to_json(OLD/NEW)但需避坑;轨迹表应统一用AFTER触发器写入JSON类型日志,主键存JSON数组,空值处理要一致。

触发器里怎么拿到变更前后的完整行数据
MySQL 5.7+ 和 PostgreSQL 都支持在 BEFORE 或 AFTER 触发器中访问 OLD 和 NEW 行,但直接转 JSON 的方式差异大,且不能简单用 JSON_OBJECT(*) —— 这个函数根本不存在。
MySQL 中必须显式列出字段拼 JSON_OBJECT,比如:
JSON_OBJECT('id', OLD.id, 'name', OLD.name, 'status', OLD.status)PostgreSQL 更灵活,可用
row_to_json(OLD) 直接转整行,但要注意:如果表含 bytea、jsonb 或自定义类型,可能报错或序列化异常。
- MySQL 不支持动态获取列名,硬编码字段是常态;加字段就得改触发器
- PostgreSQL 的
row_to_json会把 NULL 转成null(合法 JSON),但 MySQL 的JSON_OBJECT会跳过 NULL 值,导致前后结构不一致 - 别在触发器里调用存储过程做 JSON 拼装——性能陡降,且 MySQL 的触发器内不允许执行
CALL
INSERT/UPDATE/DELETE 三种操作的 JSON 记录结构怎么统一
轨迹表字段设计建议固定为:table_name、op_type('I'/'U'/'D')、pk_value(主键值,字符串化)、before_data(JSON)、after_data(JSON)、created_at。关键在于空值填充逻辑:
-
INSERT:before_data设为JSON_OBJECT()或'{}',after_data存NEW行 -
DELETE:after_data设为空对象,before_data存OLD行 -
UPDATE:两个字段都存,但注意 MySQL 中若某字段没被修改,OLD.col = NEW.col仍成立,不必过滤“未变字段”——轨迹要的是快照,不是 diff
别为了“节省体积”在触发器里做字段级 diff:既增加复杂度,又容易漏掉隐式类型转换(比如 '1' vs 1)导致误判。
MySQL 触发器写 JSON 到日志表时常见报错
最典型的是:ERROR 1442 (HY000): Can't update table 'xxx_log' in stored function/trigger because it is already used by statement which invoked this stored function/trigger。这是 MySQL 的限制:触发器不能修改当前语句正在操作的表,也不能修改它所依赖的日志表(如果日志表用了 BEFORE 触发器里的 INSERT)。
- 唯一可靠解法:日志表必须用
AFTER触发器 +INSERT ... SELECT或直接INSERT,且日志表不能有触发器 - JSON 字段用
JSON类型(MySQL 5.7+),别用TEXT——否则无法校验格式,也用不了->操作符查字段 - 如果主键是复合主键,
pk_value字段建议存 JSON 数组,如JSON_ARRAY(OLD.order_id, OLD.item_id),别拼字符串,避免分隔符冲突
PostgreSQL 中 row_to_json 的坑比你想象的多
row_to_json(OLD) 看似省事,但它默认不包含 OID、系统列,且对 jsonb 字段原样透传(可能含不可见控制字符),更麻烦的是:如果触发器所在表有 GENERATED ALWAYS AS 列,PostgreSQL 8.4 之后版本会把它也塞进 JSON,而该列值在 OLD 中不可读,导致运行时报错 cannot extract field from a scalar。
- 稳妥做法是显式构造 RECORD:用
ROW(OLD.id, OLD.name, OLD.updated_at)::record再转 JSON,避开生成列 - 时间字段注意时区:
row_to_json输出的是带时区的字符串(如"2024-06-12T15:23:44.123+08"),和应用层解析习惯是否一致得提前对齐 - 别在触发器里用
current_setting('app.user_id')记操作人——这个 GUC 变量在触发器上下文不一定可见,应由应用层写入额外字段
全量快照听着简单,但字段增删、类型变更、NULL 处理、跨数据库兼容性,每个点都会在某次上线后突然冒出来。留好触发器版本注释,比写完美代码更重要。


















