SELECT COUNT(*) FROM src_order WHERE amount IS NULL;
SELECT COUNT(*) FROM dst_order WHERE amount = 0
AND id IN (SELECT id FROM src_order WHERE amount IS NULL);

源表 amount 允许 NULL,目标表建表时写了 NOT NULL DEFAULT 0。同步之后 12 万行 NULL 变成 0,财务按目标表算出来多出 60 万营收。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。

原因不复杂,同步程序拼 insert 时按非空列过滤,NULL 值被直接跳过,落库就取列的默认值。INSERT 语句列不匹配时也会走默认值填充,而且不报错。空值和零在这条链路上彻底分不开。

「默认值覆盖真实值」排查到最后往往是很小的一处差异,前面那些复杂猜测可以先放一放。动手前把原文件复制一份带时间戳,出问题立刻换回去,比现场分析快得多。

一次只改一处,改完立刻验证,几个改动叠在一起,出问题就说不清是谁导致的。

处理上按这个顺序:目标表金额列去掉 NOT NULL 和 DEFAULT 保留 NULL 语义;同步 SQL 里把 NULL 显式写进去 不要跳过列;COALESCE 只在展示层用 不要在写入层兜底;跑一次比对把 NULL 和 0 混在一起的行捞出来。

改完只确认一件事:两边 NULL 行数相同 财务口径的营收和业务后台对齐。

作者 9823189