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