编辑 | blame | 历史 | 原始文档

汇总大表「生成数据是否正确」验证方法(2026-10-12)

用途:回答"程序生成的数据对不对"。
一句话原则:每一段都要有一个**独立于该段的参照**——两边同源,就验不出东西。
本方法是 2026-10-12 用两组真实数据跑出来并修正过的(见第七节)。

一、先把问题拆开(否则"正确"没有定义)

"程序生成的数据是否正确"实际是 4 个不同的问题,参照和责任人都不同:

# 问题 参照(必须独立于被验对象) 谁负责 程序能自证吗
1 源数据对不对 源文件 / 省里下发表模板 数据、业务 不能
2 算法、口径对不对 手写 SQL 复算(不看母版、不看定稿) 程序 能
3 写回、版式对不对 上一期母版(同源对比,要求"不该动的格未变") 程序 能
4 与人工定稿一致吗 手工定稿(必须记版本 + sha256) 双方复核 部分

所以单问"数据对不对"没有答案;先问"**哪一段**"。

二、五段链条与各自的验证手段(含失败模式)

L0 源文件 → 库

  • 参照:源文件本身。
  • 手段:行数(import_batch 的 total/success/fail vs 表内 COUNT(*));逐行抽查(每市州 1~2 条 + 边界:最大、最小、缺报市州);单位核对(吨 vs 万吨);合计勾稽(Σ17 市州 = 源表"湖北省"行)。
  • 典型失败:某月漏导;列错位;单位差 10000;重复导入叠加("某月逐市州值与相邻月完全相同"是重复导入的指纹)。
  • 工具:_compare_freight.py 那类"文件 vs 库"脚本;import_batch。

L1 库 → 口径值(**最值得投入的一层**)

  • 参照:把口径写成 SQL/脚本**自己算一遍**,不依赖母版、不依赖定稿。
  • 手段:① 异常体检 GET /api/report/summaryWorkbook/freightCheck?period=YYYY-MM(倒减为负、①漏报、Σ市州≠省行、环比异常、累计勾稽);② 缺数清单(目标期该有的月份/市州是否齐全);③ 锚点复算(全省 + 抽 2 个市州;覆盖 规上/规下/合计/周转量/中口径四维/城市内)。
  • 典型失败:规下倒减为负或虚高;全省行与 Σ市州不闭合;来源表缺期。
  • 注意:**体检通过 ≠ 数据齐全**。2026-08 体检 0 命中,但 9 月生成件的 8 月列仍空、9 月周转量仍为 0 → 异常清单与缺数清单必须**分开输出**。

L2 口径值 → 单元格(写回层)

  • 参照:上一期母版 / 上一期生成件。
  • 手段:逐格 diff,**值模式与公式模式各跑一遍**;目标月列是否真的写进去了;上期及以前列是否 0 变化。
  • 典型失败:当月列没写(本次实证:货运、网约车只回填"目标月",母版缺上月就补不上);上期列被重算冲掉;公式断链。

L3 单元格 → 整本(出表层)

  • 参照:手工定稿(**必须记录版本 + sha256**,只在定稿未滞后时有效)。
  • 手段:python docs\工具\汇总大表差异比对.py <生成件> <手工定稿> <输出前缀>;只判读 实质数值差异 / 需处理(非数值文本) 两类;差异按预登记白名单分类。
  • 典型失败:把"定稿滞后"当成"程序回归"(本次:定稿是 9/13 版,缺 09-28 之后的手工改动 → 多出 364 条手工侧错误值 + 10 条已确认项)。

L4 跨页合成页勾稽(**最容易被漏**)

  • 参照:勾稽式本身。
  • 手段:城市汇总、城市分市州、中口径明细、道路运输周转量 每页给一条勾稽式自检,例:道路运输周转量 B = G(货运页规上+规下周转量) + L/2(城市客运量) + Q/10(公路旅客周转量)。
  • 典型失败:源页都对、合成页引用错列。本次 2025-03/04 网约车拆分的影响面,就是靠"排名页 vs 明细页两条链路对不上"暴露的。

三、反同源盲区(**最重要的一条**)

今日实证:**"生成件 vs 手工定稿"这一层,在母版与定稿同源时,源数据本身的错永远查不出来。**
- 2025-11 货运宜昌周转量差 34.30(2 格)、班线包车个体周转量差(5 格),在"生成件 vs 定稿"里是 0 条,只有"**库现算 vs 母版缓存**"才暴露。
- 所以:**必须专门做一次"库内值 vs 母版缓存值"的全量 diff**——这也正是 2026-10-09 交接列为"需先做"的待办,是"能不能切读库"的唯一前置条件。
- 措辞要分清:**出表口径**上"库不能替代母版缓存"(F8/Q1);**验证方法**上必须专门比一次。两者不矛盾。

四、操作顺序(照抄即可)

  1. 冻结:记 母版 sha256、关键源表 max(report_period) 与行数、后端进程 PID + 启动时间。
    ("判断是否换了新进程"必须看 netstat -ano 的真实监听 PID + 日志启动行,不能只信启停脚本输出。)
  2. 异常体检 + 缺数清单(**分两份输出**)。
  3. 口径复算锚点(全省 + 2 市州;至少覆盖 规上/规下/合计/周转量/中口径/城市内)。
  4. 版式自检:当月列已写;上期及以前列 0 变化;#REF! 数不增;标题期次 = 目标期;累计 = 1..M。
  5. 与定稿逐格 diff(只看两类);差异按白名单分类。
  6. 合成页勾稽(L4)。
  7. 写结论:通过项 / 未通过项 / 未覆盖项(明确说清哪几层没验)。

五、通过标准(建议写死)

  • L0:行数与勾稽一致(0 差异)。
  • L1:体检 0 命中 + 缺数清单为空(或缺数已确认为业务事实)+ 锚点复算 0 差异。
  • L2:上期及以前列 0 变化;当月列写入格数 = 预期。
  • L3:定稿 diff 的"实质数值差异"只含白名单项。
  • L4:勾稽式 0 差异。
  • 任一层未覆盖 → 结论只能写"未覆盖",不得写"已验证"。

六、三条纪律

  1. 独立参照:同源的两种产物互比等于没验。
  2. 一次只换一个变量:换代码 / 换数据 / 换母版分开验,否则差异无法归因。
  3. 差异先分类再结论:结构/格式/浮点尾差只计数;只有"实质数值差异 + 需处理"进清单。白名单(USER_EDITS、已确认项)预登记,不要每次重新判读。

七、本方法的实证(2026-10-12)

  1. 2026-08 生成件 vs 9/13 手工定稿:1020 条明细里只有 11 条要关注(10 条货运 2022 累计同比【业务已确认软件对】+ 1 条手工侧脏字符),其余全是约定差异(结构/草稿列/文本型数字 354/取整尾差/手工侧错误值 364)→ 算法层通过。
  2. 库内 2025 vs 母版 2025:公交、出租、轨道轮渡 100% 一致;货运、班线包车除个别格一致;查出 3 处需业务确认:
  • 网约车 2025-03/04「其中城市内」口径 60 格(另有 12 月 12 格小差)——2026-09-21 已定位(母版用了另一份旧的出租城市内输入);
  • 货运 宜昌 2025-11 周转量 2 格(差 34.30);
  • 班线包车 2025-11 个体旅客周转量 5 格。
    后两处为**源数据层新发现**,此前无任何记录(因为历史核对都在同源的"生成件 vs 定稿"层)。