1.0.467.sql 5.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. -- ============================================================================
  2. -- S6-DISPLAY-FIX-B1 · D-04 存量订正:mdp_std_ipqc_inspection 字面量 'null' → SQL NULL
  3. --
  4. -- 背景(根因已在同批代码修复):
  5. -- IpqcInspectionMdpSyncService.TransformHeadStandardAsync 原先对字符串列使用裸
  6. -- JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.x'))。MySQL 下 JSON null 经 JSON_UNQUOTE
  7. -- 会产出**长度为 4 的字面量字符串 'null'**(实测:其 IS NULL = 0),
  8. -- 于是 API 返回 "formNo":"null",前端 `|| '-'` 兜底对真值字符串无效,页面直接显示 null。
  9. -- 同文件的 detail transform 因使用 MdpJsonSql.Str()(= NULLIF(...,'null'))从未受影响。
  10. -- 代码侧已改为统一走 MdpJsonSql.Str/Dec/DateTimeSec,此后不再产生新污染;
  11. -- 本脚本只订正**已经落库的存量**。
  12. --
  13. -- 证据(aidopdev,2026-09-02 只读取证):
  14. -- ① 受影响 10 列,7/8 行命中(form_no / version_no / inspec_standard_code /
  15. -- inspec_standard_version / production_person / attachment 各 7 行,remark 3 行,
  16. -- process_code / process_name / production_work_order 各 2 行)。
  17. -- ② 逐行 JOIN 回源表 qms_gcjyd 证明:凡 STD 为 'null' 者,其源列**均为 SQL NULL**,
  18. -- 不存在"业务真的存了文本 null"的反例。
  19. -- ③ 全表扫描 qms_gcjyd 的 17 个对应源列,**没有任何一列持有字面量文本 'null'**。
  20. -- 故 'null' 100% 来自转换污染,可安全归一为 SQL NULL。
  21. --
  22. -- 写入范围(INSERT 0 · UPDATE ≤8 行 · DELETE 0 · DDL 0):
  23. -- 表:mdp_std_ipqc_inspection(仅标准层,不碰任何业务源表)
  24. -- 列白名单(10 列,均为可空展示列;不含 NOT NULL 列,不含身份键):
  25. -- attachment, form_no, inspec_standard_code, inspec_standard_version,
  26. -- process_code, process_name, production_person, production_work_order,
  27. -- remark, version_no
  28. -- 行范围:仅限**能回溯到源单**的行(EXISTS qms_gcjyd 同 id 同 tenant_id)。
  29. --
  30. -- 刻意不处理(无法自证,遵循"证明不了就不清洗"):
  31. -- 2 行孤儿 STD(source_row_id 在 qms_gcjyd 无对应行)——
  32. -- id=22(source_system=WMS_API 契约测试夹具,tenant 1300000000001)与
  33. -- id=32(tenant_id=0,本身即已知脏行)。
  34. -- 这两行的贴源记录仍在 mdp_stg_ipqc_pull 中,**下一次 inbound 会由已修复的转换代码
  35. -- 自动 upsert 订正**,无需在此强行 UPDATE。
  36. --
  37. -- 幂等:WHERE col='null' 决定重复执行为 0 行影响。
  38. -- 回滚:本操作把污染值归一为源真值(NULL),属**数据订正而非数据变更**,不提供反向脚本
  39. -- (反向写回 'null' 只会复原缺陷)。如确需还原,可对同一行集执行
  40. -- UPDATE ... SET col='null' WHERE col IS NULL,但需先确认该列源值确为 NULL。
  41. -- ============================================================================
  42. -- ── 执行前计数(期望:与上文证据一致)──
  43. SELECT 'BEFORE' AS phase,
  44. SUM(attachment = 'null') AS attachment,
  45. SUM(form_no = 'null') AS form_no,
  46. SUM(inspec_standard_code = 'null') AS inspec_standard_code,
  47. SUM(inspec_standard_version = 'null') AS inspec_standard_version,
  48. SUM(process_code = 'null') AS process_code,
  49. SUM(process_name = 'null') AS process_name,
  50. SUM(production_person = 'null') AS production_person,
  51. SUM(production_work_order = 'null') AS production_work_order,
  52. SUM(remark = 'null') AS remark,
  53. SUM(version_no = 'null') AS version_no
  54. FROM mdp_std_ipqc_inspection;
  55. -- ── 订正:仅可回溯到源单的行,且仅当该列恰为字面量 'null' ──
  56. UPDATE mdp_std_ipqc_inspection m
  57. SET m.attachment = NULLIF(m.attachment, 'null'),
  58. m.form_no = NULLIF(m.form_no, 'null'),
  59. m.inspec_standard_code = NULLIF(m.inspec_standard_code, 'null'),
  60. m.inspec_standard_version = NULLIF(m.inspec_standard_version, 'null'),
  61. m.process_code = NULLIF(m.process_code, 'null'),
  62. m.process_name = NULLIF(m.process_name, 'null'),
  63. m.production_person = NULLIF(m.production_person, 'null'),
  64. m.production_work_order = NULLIF(m.production_work_order, 'null'),
  65. m.remark = NULLIF(m.remark, 'null'),
  66. m.version_no = NULLIF(m.version_no, 'null')
  67. WHERE EXISTS (
  68. SELECT 1 FROM qms_gcjyd s
  69. WHERE s.id = m.source_row_id AND s.tenant_id = m.tenant_id
  70. )
  71. AND (m.attachment = 'null' OR m.form_no = 'null'
  72. OR m.inspec_standard_code = 'null' OR m.inspec_standard_version = 'null'
  73. OR m.process_code = 'null' OR m.process_name = 'null'
  74. OR m.production_person = 'null' OR m.production_work_order = 'null'
  75. OR m.remark = 'null' OR m.version_no = 'null');
  76. -- ── 执行后计数(期望:可回溯行全部归零;仅可能残留上文声明的 2 行孤儿)──
  77. SELECT 'AFTER' AS phase,
  78. SUM(attachment = 'null') AS attachment,
  79. SUM(form_no = 'null') AS form_no,
  80. SUM(inspec_standard_code = 'null') AS inspec_standard_code,
  81. SUM(inspec_standard_version = 'null') AS inspec_standard_version,
  82. SUM(process_code = 'null') AS process_code,
  83. SUM(process_name = 'null') AS process_name,
  84. SUM(production_person = 'null') AS production_person,
  85. SUM(production_work_order = 'null') AS production_work_order,
  86. SUM(remark = 'null') AS remark,
  87. SUM(version_no = 'null') AS version_no
  88. FROM mdp_std_ipqc_inspection;