dispatch_refactor_fix_number.sql 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /* =====================================================================
  2. 应急与指挥调度模块改造 · 业务编号 / 分级状态 修复脚本
  3. =====================================================================
  4. 文件:dispatch_refactor_fix_number.sql
  5. 用途:修补 dispatch_refactor_migrate.sql「三、四」两段因方言问题未生效的部分。
  6. ---------------------------------------------------------------------
  7. 背景:本实例的 KingbaseES 处于 MySQL 兼容语义,`||` 被解析为「逻辑或」
  8. (boolean OR),不是字符串连接符。实测:
  9. SELECT 'GJ' || '20261014' || '001'; -- 返回 boolean t
  10. SELECT concat('GJ','20261014','001'); -- 返回 GJ20261014001
  11. 于是 migrate 里用 `||` 生成业务编号的语句:
  12. · dis_event_grade —— 整列被写成字符串 'true',第 2 行即撞
  13. uk_dis_event_grade_no,报「重复键违反唯一约束」并整段回滚;
  14. · dis_plan —— 同理(uk_dis_plan_no),或在只有 1 行时
  15. 静默写入一行脏值 'true'。
  16. ---------------------------------------------------------------------
  17. 为什么不能靠「重跑 migrate」修:
  18. migrate 三段的主表搬迁 INSERT 带 `WHERE NOT EXISTS (... plan_name 相同)`,
  19. 已经插进去的那一行(plan_no = 'true')会被去重跳过,编号永远停错。
  20. 所以需要本脚本按 id 逐行补号。
  21. ---------------------------------------------------------------------
  22. 本脚本做什么(全部幂等,可重复执行;不删除任何数据):
  23. 1) 清脏编号:plan_no / grade_no 中不符合规范的(如 'true')置为 NULL
  24. 2) 事件分级状态归一:中文 → 0/1,并把 status 列类型从 varchar 改成 int2
  25. 3) 分级的口径映射:旧列 category/level(若还在)→ 新列;中文等级/类别 → 数字
  26. 4) 重新取号:YA+yyyyMMdd+4位序号 / GJ+yyyyMMdd+3位序号(按 id 顺序)
  27. 5) dis_plan_version.plan_no 与主表对齐
  28. 注意:4) 已统一改用 concat(),不再使用 `||`。
  29. 执行前提:dispatch_refactor.sql 已执行(新表 + 唯一索引 + 字典已就绪)。
  30. 执行方式:本脚本只有一个 BEGIN; ... COMMIT;,可整段选中运行。
  31. ===================================================================== */
  32. BEGIN;
  33. /* ---------------------------------------------------------------------
  34. 1) 清脏编号:非规范编号一律置 NULL,交给第 4 步重新生成
  35. YA 规范:YA + 8 位日期 + 4 位序号 = YA + 12 位数字
  36. GJ 规范:GJ + 8 位日期 + 3 位序号 = GJ + 11 位数字
  37. --------------------------------------------------------------------- */
  38. UPDATE app_user.dis_plan
  39. SET plan_no = NULL
  40. WHERE plan_no IS NOT NULL
  41. AND plan_no !~ '^YA[0-9]{12}$';
  42. UPDATE app_user.dis_event_grade
  43. SET grade_no = NULL
  44. WHERE grade_no IS NOT NULL
  45. AND grade_no !~ '^GJ[0-9]{11}$';
  46. /* ---------------------------------------------------------------------
  47. 2) 事件分级状态归一:启用/停用 → 0/1,并把列类型改成 int2
  48. (migrate 四段整体回滚,所以 status 现在很可能还是 varchar + 中文;
  49. 后端按 Integer 处理,WHERE status = 2 打在 varchar 列上会直接报类型错)
  50. --------------------------------------------------------------------- */
  51. UPDATE app_user.dis_event_grade SET status = '0' WHERE status::text IN ('启用', '正常');
  52. UPDATE app_user.dis_event_grade SET status = '1' WHERE status::text IN ('停用', '禁用');
  53. UPDATE app_user.dis_event_grade SET status = '0' WHERE status::text !~ '^[0-9]+$';
  54. DO $$
  55. BEGIN
  56. IF EXISTS (SELECT 1 FROM information_schema.columns
  57. WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade'
  58. AND column_name = 'status' AND data_type <> 'smallint') THEN
  59. EXECUTE 'ALTER TABLE app_user.dis_event_grade ALTER COLUMN status TYPE int2 USING status::int2';
  60. END IF;
  61. END $$;
  62. /* ---------------------------------------------------------------------
  63. 3) 分级口径映射(旧列若已被 drop 删除,本段自动跳过,是安全的空操作)
  64. --------------------------------------------------------------------- */
  65. DO $$
  66. BEGIN
  67. -- 3.1 旧列 category / level → 新列(必须早于下面的口径映射)
  68. IF EXISTS (SELECT 1 FROM information_schema.columns
  69. WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade' AND column_name = 'category') THEN
  70. EXECUTE 'UPDATE app_user.dis_event_grade SET event_category = category WHERE event_category IS NULL AND category IS NOT NULL';
  71. END IF;
  72. IF EXISTS (SELECT 1 FROM information_schema.columns
  73. WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade' AND column_name = 'level') THEN
  74. EXECUTE 'UPDATE app_user.dis_event_grade SET event_level = "level" WHERE event_level IS NULL AND "level" IS NOT NULL';
  75. END IF;
  76. END $$;
  77. -- 3.2 等级口径:旧 4 级 → 新 3 级
  78. UPDATE app_user.dis_event_grade SET event_level = '1'
  79. WHERE event_level IN ('一级', 'Ⅰ级', '特别重大', '重大', '紧急');
  80. UPDATE app_user.dis_event_grade SET event_level = '2'
  81. WHERE event_level IN ('二级', 'Ⅱ级', '较大');
  82. UPDATE app_user.dis_event_grade SET event_level = '3'
  83. WHERE event_level IN ('三级', '四级', 'Ⅲ级', 'Ⅳ级', '一般');
  84. -- 3.3 类别口径
  85. UPDATE app_user.dis_event_grade SET event_category = '1' WHERE event_category IN ('管网泄漏', '管道泄漏');
  86. UPDATE app_user.dis_event_grade SET event_category = '2' WHERE event_category IN ('路面积水', '积水');
  87. UPDATE app_user.dis_event_grade SET event_category = '3' WHERE event_category IN ('设施损坏', '塌陷');
  88. UPDATE app_user.dis_event_grade SET event_category = '4' WHERE event_category IN ('管道堵塞', '堵塞');
  89. /* ---------------------------------------------------------------------
  90. 4) 重新取号
  91. 说明:子查询读的是语句开始时的快照,row_number() 按 id 排序;
  92. NOT EXISTS 逐个比对,运行期已占用的编号直接跳过(该行保持
  93. NULL,由页面人工补号),确保不会撞 uk_dis_plan_no /
  94. uk_dis_event_grade_no。
  95. CURRENT_TIMESTAMP 在同一事务内是常量,同批日期段一致。
  96. --------------------------------------------------------------------- */
  97. -- 4.1 dis_plan:YA + yyyyMMdd + 4 位序号
  98. UPDATE app_user.dis_plan g
  99. SET plan_no = c.new_no
  100. FROM (SELECT t.id,
  101. concat('YA', to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'),
  102. lpad(CAST(t.rn AS varchar), 4, '0')) AS new_no
  103. FROM (SELECT id, row_number() OVER (ORDER BY id) AS rn
  104. FROM app_user.dis_plan
  105. WHERE plan_no IS NULL) t) c
  106. WHERE g.id = c.id
  107. AND NOT EXISTS (SELECT 1 FROM app_user.dis_plan x WHERE x.plan_no = c.new_no);
  108. -- 4.2 dis_event_grade:GJ + yyyyMMdd + 3 位序号
  109. UPDATE app_user.dis_event_grade g
  110. SET grade_no = c.new_no
  111. FROM (SELECT t.id,
  112. concat('GJ', to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'),
  113. lpad(CAST(t.rn AS varchar), 3, '0')) AS new_no
  114. FROM (SELECT id, row_number() OVER (ORDER BY id) AS rn
  115. FROM app_user.dis_event_grade
  116. WHERE grade_no IS NULL) t) c
  117. WHERE g.id = c.id
  118. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_grade x WHERE x.grade_no = c.new_no);
  119. /* ---------------------------------------------------------------------
  120. 5) 版本快照的编号与主表对齐
  121. 只处理主表已有编号的版本行;重复执行结果一致(幂等)。
  122. --------------------------------------------------------------------- */
  123. UPDATE app_user.dis_plan_version v
  124. SET plan_no = d.plan_no
  125. FROM app_user.dis_plan d
  126. WHERE v.plan_id = d.id
  127. AND d.plan_no IS NOT NULL
  128. AND (v.plan_no IS NULL OR v.plan_no <> d.plan_no);
  129. COMMIT;
  130. /* =====================================================================
  131. 6) 结果自检(只读)—— 请核对:
  132. · dis_event_grade:status 只应是 0/1;grade_no 形如 GJ20261014001
  133. · dis_plan :plan_no 形如 YA202610140001
  134. · 都不应出现 'true'
  135. ===================================================================== */
  136. SELECT id, grade_no, event_category, event_level, status
  137. FROM app_user.dis_event_grade ORDER BY id;
  138. SELECT id, plan_no, plan_name, status
  139. FROM app_user.dis_plan ORDER BY id;
  140. /* 若下面这条返回 0 行,说明编号已全部规范 */
  141. SELECT 'dis_plan' AS tbl, count(*) AS bad FROM app_user.dis_plan
  142. WHERE plan_no IS NOT NULL AND plan_no !~ '^YA[0-9]{12}$'
  143. UNION ALL
  144. SELECT 'dis_event_grade', count(*) FROM app_user.dis_event_grade
  145. WHERE grade_no IS NOT NULL AND grade_no !~ '^GJ[0-9]{11}$';