1.0.466.sql 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. -- ============================================================================
  2. -- S5-IQC-B1 · 原材料检规 ↔ 物料 派生映射桥表
  3. --
  4. -- 目标:为 S5 IQC 建立 qms_jygf → ItemMaster 的稳定映射基础设施。
  5. -- 本批 **不改变任何现有业务写路径行为**:不动 qms_qcp_inspbill、
  6. -- 不动 GenerateInspBill、不动 qms_jygf / qms_jygfzb、不动报检数据。
  7. --
  8. -- ⚠️ 数据回填范围:**仅 UAT-A 租户(SysOrg.Code = 'UATTEST_CHL')**
  9. -- ⚠️ 明确不回填 797403760988229 的 4704 条历史检规,不治理其他租户。
  10. -- ⚠️ 表结构本身是多租户通用的;只有下方 ③ 的 seed 是 tenant scoped。
  11. --
  12. -- 写入范围:CREATE TABLE 1 · INSERT(仅 UAT-A) · UPDATE 0 · DELETE 0 · 其他 DDL 0
  13. --
  14. -- ---------------------------------------------------------------------------
  15. -- 【分词等价性保证 —— 本脚本最重要的约束】
  16. --
  17. -- 运行期分词口径唯一定义在 C# S5IqcMaterialTokenizer:
  18. -- 分隔符 = ; ; , , 、 : : / 空格 Tab CR LF (**不含 . - _**,它们出现在合法物料码内部)
  19. -- 标准化 = TRIM + UPPER
  20. -- 分级 = EXACT(命中 ItemMaster) / NOT_FOUND / GLUE_UNRESOLVED
  21. --
  22. -- SQL 无法可靠复刻上述正则。为杜绝「迁移一套规则、运行期另一套规则」,本脚本
  23. -- **只为不含任何分隔符的单 token 检规生成映射**(见 ③ 的 NOT LIKE 守卫)——
  24. -- 此时分词退化为恒等变换 TRIM(UPPER(wlbm)),SQL 与 C# 结果**可证明相同**。
  25. -- 含分隔符的检规一律不由本脚本落地,必须调用 POST /api/S5IqcSpecMap/rebuild 生成。
  26. --
  27. -- 实测(2026-09-02)UAT-A 8 条检规的 wlbm 全部无分隔符 → 本脚本覆盖 8/8,无遗漏。
  28. -- 该守卫同时保证:即使将来 UAT-A 新增了多物料检规,脚本也只会「少做」不会「做错」。
  29. -- ---------------------------------------------------------------------------
  30. --
  31. -- 幂等:CREATE TABLE IF NOT EXISTS + INSERT ... WHERE NOT EXISTS(唯一业务键)。
  32. -- 重复执行结果恒等,不产生重复行。
  33. -- ============================================================================
  34. -- ===========================================================================
  35. -- ① 桥表
  36. -- 唯一约束 uk_s5_iqc_map(tenant_id, spec_id, raw_token, is_manual):
  37. -- raw_token 决定 material_code,故该组合即「spec_id + material_code 唯一」的
  38. -- 等价防重复机制,且在 material_code 为 NULL(NOT_FOUND/GLUE) 时依然有效
  39. -- (MySQL 唯一索引允许多个 NULL,若用 material_code 做键将无法防重)。
  40. -- ===========================================================================
  41. CREATE TABLE IF NOT EXISTS ado_s5_iqc_spec_material_map (
  42. id BIGINT NOT NULL COMMENT '主键(雪花)',
  43. tenant_id BIGINT NOT NULL COMMENT '租户ID',
  44. spec_id BIGINT NOT NULL COMMENT '原材料检规ID → qms_jygf.id',
  45. spec_no VARCHAR(100) NULL COMMENT '检规文件编号 wjbh(冗余,随主表同步刷新)',
  46. spec_version VARCHAR(50) NULL COMMENT '检规版本 bb(冗余,随主表同步刷新)',
  47. seq INT NOT NULL DEFAULT 0 COMMENT 'token 在 wlbm 中的序号;人工绑定行为 0',
  48. raw_token VARCHAR(200) NOT NULL COMMENT 'wlbm 切分出的原始片段(保留原貌供审计)',
  49. material_code VARCHAR(100) NULL COMMENT '标准化后的物料码;仅 EXACT/MANUAL 落值,其余 NULL(绝不猜物料)',
  50. match_status VARCHAR(20) NOT NULL COMMENT 'EXACT / MANUAL / NOT_FOUND / GLUE_UNRESOLVED',
  51. source_raw TEXT NULL COMMENT '检规 wlbm 原始全串(审计用)',
  52. is_manual TINYINT NOT NULL DEFAULT 0 COMMENT '1=人工绑定,自动同步永不删除',
  53. note VARCHAR(500) NULL COMMENT '人工绑定备注',
  54. create_time DATETIME NULL,
  55. update_time DATETIME NULL,
  56. create_user_id BIGINT NULL,
  57. PRIMARY KEY (id),
  58. UNIQUE KEY uk_s5_iqc_map (tenant_id, spec_id, raw_token, is_manual),
  59. KEY idx_s5_iqc_map_item (tenant_id, material_code),
  60. KEY idx_s5_iqc_map_spec (tenant_id, spec_id)
  61. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
  62. COMMENT='S5 IQC 原材料检规↔物料 派生映射(B-1)';
  63. -- ===========================================================================
  64. -- ② 目标租户解析(UAT-A)。解析不到则下方 INSERT 全部为空,脚本安全空转。
  65. -- ===========================================================================
  66. SET @uat_a := (SELECT t.Id FROM SysTenant t JOIN SysOrg o ON o.Id = t.OrgId
  67. WHERE o.Code = 'UATTEST_CHL' AND t.Status = 1 LIMIT 1);
  68. -- ===========================================================================
  69. -- ③ UAT-A 回填(仅无分隔符的单 token 检规;仅 EXACT/NOT_FOUND 两态)
  70. --
  71. -- 分隔符守卫:wlbm 不得含 ; ; , , 、 : : / 空格 Tab CR LF
  72. -- ('\\' 为 MySQL LIKE 默认转义符,此处无需转义任何目标字符)
  73. -- GLUE_UNRESOLVED 在「单 token」前提下只可能因长度越界或含中文产生,
  74. -- 本脚本对这两种情况也如实落 GLUE_UNRESOLVED,与 C# Classify 完全一致。
  75. -- ===========================================================================
  76. INSERT INTO ado_s5_iqc_spec_material_map
  77. (id, tenant_id, spec_id, spec_no, spec_version, seq, raw_token, material_code,
  78. match_status, source_raw, is_manual, note, create_time, update_time, create_user_id)
  79. SELECT
  80. -- 确定性 ID = 3000000000000030000 + 该 spec 在本租户 qms_jygf 中按 id 升序的稳定排名。
  81. -- 【为什么不用 @rn := @rn + 1】MySQL 8 明确规定 SELECT 中用户变量的求值顺序未定义,
  82. -- 实测会产生重复值并触发 PRIMARY 冲突(1062)。
  83. -- 【为什么不用 ROW_NUMBER()】窗口序号是对**过滤后**结果集编号,重跑时若部分行已存在,
  84. -- 剩余行会被重新编号 → 与首次写入的 id 冲突。
  85. -- 相关子查询排名只依赖 qms_jygf 自身内容,与本表已有多少行无关,故重跑恒等。
  86. 3000000000000030000 + (
  87. SELECT COUNT(*) FROM qms_jygf r
  88. WHERE r.tenant_id = g.tenant_id AND r.id <= g.id
  89. ),
  90. g.tenant_id,
  91. g.id,
  92. g.wjbh,
  93. g.bb,
  94. 1,
  95. TRIM(g.wlbm),
  96. CASE WHEN im.ItemNum IS NOT NULL THEN UPPER(TRIM(g.wlbm)) ELSE NULL END,
  97. CASE
  98. WHEN im.ItemNum IS NOT NULL THEN 'EXACT'
  99. WHEN CHAR_LENGTH(TRIM(g.wlbm)) < 4 OR CHAR_LENGTH(TRIM(g.wlbm)) > 16 THEN 'GLUE_UNRESOLVED'
  100. WHEN TRIM(g.wlbm) REGEXP '[\\x{4E00}-\\x{9FFF}]' THEN 'GLUE_UNRESOLVED'
  101. ELSE 'NOT_FOUND'
  102. END,
  103. g.wlbm,
  104. 0,
  105. NULL,
  106. NOW(), NOW(), NULL
  107. FROM qms_jygf g
  108. LEFT JOIN ItemMaster im
  109. ON im.tenant_id = g.tenant_id
  110. AND im.ItemNum = UPPER(TRIM(g.wlbm))
  111. WHERE @uat_a IS NOT NULL
  112. AND g.tenant_id = @uat_a
  113. AND g.wlbm IS NOT NULL
  114. AND TRIM(g.wlbm) <> ''
  115. -- 分词等价性守卫:只处理无分隔符的单 token
  116. AND TRIM(g.wlbm) NOT LIKE '%;%'
  117. AND TRIM(g.wlbm) NOT LIKE '%;%'
  118. AND TRIM(g.wlbm) NOT LIKE '%,%'
  119. AND TRIM(g.wlbm) NOT LIKE '%,%'
  120. AND TRIM(g.wlbm) NOT LIKE '%、%'
  121. AND TRIM(g.wlbm) NOT LIKE '%:%'
  122. AND TRIM(g.wlbm) NOT LIKE '%:%'
  123. AND TRIM(g.wlbm) NOT LIKE '%/%'
  124. AND TRIM(g.wlbm) NOT LIKE '% %'
  125. AND TRIM(g.wlbm) NOT LIKE CONCAT('%', CHAR(9), '%')
  126. AND TRIM(g.wlbm) NOT LIKE CONCAT('%', CHAR(13), '%')
  127. AND TRIM(g.wlbm) NOT LIKE CONCAT('%', CHAR(10), '%')
  128. -- 幂等:同 (tenant, spec, raw_token, is_manual) 已存在则跳过
  129. AND NOT EXISTS (
  130. SELECT 1 FROM ado_s5_iqc_spec_material_map x
  131. WHERE x.tenant_id = g.tenant_id
  132. AND x.spec_id = g.id
  133. AND x.raw_token = TRIM(g.wlbm)
  134. AND x.is_manual = 0);