-- ===================================================================== -- 应急与指挥调度模块(dispatch)改造脚本 —— 存量数据搬迁 -- 数据库:KingbaseES(PG 兼容模式),业务 schema = app_user -- -- ⚠️ 使用前提:**只在有存量数据的环境执行**。 -- 本脚本依赖旧表 / 旧列(dis_event.source、dis_event."level"、dis_event_report、 -- dis_event_verify、dis_event_transfer_log、dis_event_track_log、 -- dis_event_urge(_detail)、dis_event_plan、dis_plan_approval 等)。 -- 全新库请勿执行 —— 所有块都用 to_regclass / information_schema 做了存在性判断, -- 缺失时自动跳过,但空跑一遍也没有意义。 -- -- 执行顺序:先 dispatch_refactor.sql(建表 + 加列 + 字典),再执行本脚本。 -- -- 幂等性:所有 INSERT 带 NOT EXISTS 去重;所有 UPDATE 带状态判断,可重复执行。 -- 破坏性:无。本脚本不删除任何旧表、旧列、旧数据。 -- -- ⚠️ 执行注意(KingbaseES 客户端 · 事务挂起): -- 本脚本按「一 / 二 / 三 / 四」分成 4 个独立事务(每段 END $$; 后即 COMMIT;), -- 某段失败只回滚该段,前面已成功的段落保持落库。 -- 但失败后当前连接仍处于 aborted 事务中,此后**所有**查询(包括只读 SELECT) -- 都会报「ERROR: 当前事务被终止, 事务块结束之前的查询被忽略」。 -- 处理办法:先单独执行 ROLLBACK; 再排查失败语句;不要反复重跑。 -- -- ⚠️ 执行注意(客户端把注释行与语句拼在一起): -- 部分客户端在「选中运行」时会把紧邻的 `--` 注释行并入语句并吃掉一个减号, -- 报 `ERROR: 语法错误 在 "." 或附近的`。逐条执行时请**只选中 SQL 本体**。 -- -- ⚠️⚠️ 方言红线(本实例实测,务必遵守):**不要用 `||` 做字符串拼接**。 -- 本实例的 KingbaseES 处于 MySQL 兼容语义,`||` 被解析为**逻辑或(boolean OR)** -- 而不是字符串连接符。实测: -- SELECT 'GJ' || '20261014' || '001'; -- 返回 boolean t (错) -- SELECT concat('GJ','20261014','001'); -- 返回 GJ20261014001(对) -- 后果:`SET grade_no = 'GJ' || to_char(...)` 会把 16 行全写成一个值 `'true'`, -- 第 2 行即撞唯一索引 uk_dis_event_grade_no,报 -- ERROR: 重复键违反唯一约束 / DETAIL: 键值 "(grade_no)=(true)" 已经存在。 -- 凡拼接一律改用 concat(a, b, c);需要定宽时配 lpad(CAST(x AS varchar), n, '0')。 -- ===================================================================== -- ===================================================================== -- 零、前置体检(只读,先跑这里看一眼输出再往下) -- 作用:把「搬迁源还在不在」显式喊出来。 -- 本脚本内部的搬迁块都用 to_regclass 做存在性判断,旧表/旧列不存在时**静默跳过**, -- 这是为了幂等;但代价是:若 drop 脚本被提前执行(顺序颠倒), -- 整个搬迁会一声不响地全部跳过,脚本却显示"成功"。 -- 本段会在那种情况下直接抛 WARNING,避免数据悄悄丢。 -- ===================================================================== DO $$ DECLARE n_old int; src text; BEGIN SELECT count(*), string_agg(t.n, ', ' ORDER BY t.n) INTO n_old, src FROM (VALUES ('dis_event_report'), ('dis_event_verify'), ('dis_event_transfer'), ('dis_event_transfer_log'), ('dis_event_track'), ('dis_event_track_log'), ('dis_event_urge'), ('dis_event_urge_detail'), ('dis_event_record'), ('dis_event_plan'), ('dis_plan_approval'), ('dis_plan_approval_step'), ('dis_plan_category') ) AS t(n) WHERE to_regclass(concat('app_user.', t.n)) IS NOT NULL; IF n_old = 0 THEN RAISE WARNING '搬迁源体检:未检测到任何旧表,本脚本将【不搬迁任何数据】(所有段都会跳过,但脚本仍显示成功)。'; RAISE WARNING '可能原因:drop 脚本被提前执行,旧表已被删除 —— 被删的旧表正是本脚本唯一的数据来源。'; RAISE WARNING '正确顺序是 建表 -> migrate(本脚本) -> 业务验证 -> drop;顺序颠倒会永久丢失存量数据。'; ELSE RAISE NOTICE '搬迁源体检:检测到 % 张旧表(%),将执行搬迁。', n_old, src; END IF; IF to_regclass('app_user.dis_event_grade') IS NOT NULL AND EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade' AND column_name = 'category') THEN RAISE NOTICE 'detected: dis_event_grade 旧列 category / level 尚在,第 4.1 步可正常搬值。'; ELSE RAISE NOTICE 'dis_event_grade 旧列 category / level 已不存在(或表不存在),第 4.1 步会跳过,event_category / event_level 将保持为空。'; END IF; END $$; BEGIN; -- ===================================================================== -- 一、dis_event 状态归一 + 旧列搬迁 -- 旧 status(中文串):待受理/待核实/已核实/处置中/已解除/已关闭/不实事件 -- 新 status(int2) :0 待核实 / 1 已核实 / 2 处置中 / 3 已解除 / 4 不实事件 -- ===================================================================== DO $$ DECLARE has_source boolean; has_level boolean; has_category boolean; has_close_remark boolean; BEGIN SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'source') INTO has_source; SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'level') INTO has_level; SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'category') INTO has_category; SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'close_remark') INTO has_close_remark; -- 1.1 状态归一:只处理仍是中文串的行,重复执行不会二次改写 UPDATE app_user.dis_event SET status = '0' WHERE status::text IN ('待受理', '待核实'); UPDATE app_user.dis_event SET status = '1' WHERE status::text = '已核实'; UPDATE app_user.dis_event SET status = '2' WHERE status::text = '处置中'; UPDATE app_user.dis_event SET status = '3' WHERE status::text IN ('已解除', '已关闭'); UPDATE app_user.dis_event SET status = '4' WHERE status::text = '不实事件'; -- 兜底:任何未识别的历史状态归 0(待核实),避免后续类型转换失败 UPDATE app_user.dis_event SET status = '0' WHERE status::text !~ '^[0-9]+$'; -- 1.1b 列类型收敛:varchar → int2 -- 必须在状态归一之后执行,否则中文值无法直接转型。 -- 已是 int2 的库跳过。 IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'status' AND data_type <> 'smallint') THEN EXECUTE 'ALTER TABLE app_user.dis_event ALTER COLUMN status TYPE int2 USING status::int2'; END IF; -- 1.2 旧列 → 新列(仅当新列为空) IF has_source THEN EXECUTE 'UPDATE app_user.dis_event SET event_source = source WHERE event_source IS NULL AND source IS NOT NULL'; EXECUTE $q$UPDATE app_user.dis_event SET event_source = '1' WHERE event_source = '巡查上报'$q$; EXECUTE $q$UPDATE app_user.dis_event SET event_source = '2' WHERE event_source = '呼叫中心'$q$; EXECUTE $q$UPDATE app_user.dis_event SET event_source = '3' WHERE event_source = '视频监控'$q$; EXECUTE $q$UPDATE app_user.dis_event SET event_source = '4' WHERE event_source = '上级下达'$q$; END IF; IF has_level THEN EXECUTE 'UPDATE app_user.dis_event SET event_level = "level" WHERE event_level IS NULL AND "level" IS NOT NULL'; -- 旧 4 级 → 新 3 级口径映射;执行前请按业务确认,以下为默认映射 EXECUTE $q$UPDATE app_user.dis_event SET event_level = '1' WHERE event_level IN ('特别重大', '重大', '一级', 'Ⅰ级', '紧急')$q$; EXECUTE $q$UPDATE app_user.dis_event SET event_level = '2' WHERE event_level IN ('较大', '二级', 'Ⅱ级')$q$; EXECUTE $q$UPDATE app_user.dis_event SET event_level = '3' WHERE event_level IN ('一般', '三级', '四级', 'Ⅲ级', 'Ⅳ级')$q$; END IF; IF has_category THEN EXECUTE 'UPDATE app_user.dis_event SET event_category = category WHERE event_category IS NULL AND category IS NOT NULL'; EXECUTE $q$UPDATE app_user.dis_event SET event_category = '1' WHERE event_category = '管道泄漏'$q$; EXECUTE $q$UPDATE app_user.dis_event SET event_category = '2' WHERE event_category IN ('路面积水', '积水')$q$; EXECUTE $q$UPDATE app_user.dis_event SET event_category = '3' WHERE event_category IN ('设施损坏', '塌陷')$q$; EXECUTE $q$UPDATE app_user.dis_event SET event_category = '4' WHERE event_category IN ('管道堵塞', '堵塞')$q$; END IF; -- 1.3 解除说明兜底 IF has_close_remark THEN EXECUTE 'UPDATE app_user.dis_event SET release_note = close_remark WHERE release_note IS NULL AND close_remark IS NOT NULL'; END IF; -- 1.4 状态为终态但缺少时间列的兜底 UPDATE app_user.dis_event SET dispose_time = COALESCE(dispose_time, report_time) WHERE status IN (2, 3) AND dispose_time IS NULL; UPDATE app_user.dis_event SET release_time = COALESCE(release_time, update_time, report_time) WHERE status = 3 AND release_time IS NULL; END $$; -- 分段提交:第一段成功即落库;后续段落失败时不会把本段一起回滚。 COMMIT; BEGIN; -- ===================================================================== -- 二、各子表 → dis_event_log -- 旧子表保留不动;待新结构验证通过后由 dispatch_refactor_drop.sql 处理。 -- 对应关系: -- dis_event_report → oper_type 0 接报登记 -- dis_event_verify → oper_type 1/2 初判核实 / 不实核销 -- dis_event_transfer_log → oper_type 3/4 电话转接 / 上报报送 -- dis_event_track_log → oper_type 6 处置跟踪 -- dis_event_urge_detail → oper_type 8 工作催办 -- 由状态轨迹推导 → oper_type 5 开始处置 / 7 事件解除 -- ===================================================================== DO $$ BEGIN -- 2.1 接报登记 IF to_regclass('app_user.dis_event_report') IS NOT NULL THEN INSERT INTO app_user.dis_event_log (event_id, event_no, oper_type, oper_desc, status_to, oper_time, oper_user_name) SELECT e.id, r.event_no, 0, '接报登记(历史数据迁移)', 0, r.report_time, r.reporter FROM app_user.dis_event_report r JOIN app_user.dis_event e ON e.event_no = r.event_no WHERE r.event_no IS NOT NULL AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log l WHERE l.event_no = r.event_no AND l.oper_type = 0); END IF; -- 2.2 初判核实 / 不实核销 IF to_regclass('app_user.dis_event_verify') IS NOT NULL THEN INSERT INTO app_user.dis_event_log (event_id, event_no, oper_type, oper_desc, status_from, status_to, oper_time, oper_user_name) SELECT e.id, v.event_no, CASE WHEN v.is_real = '不实' THEN 2 ELSE 1 END, COALESCE(v.opinion, '初判核实(历史数据迁移)'), 0, CASE WHEN v.is_real = '不实' THEN 4 ELSE 1 END, v.verify_time, v.verifier FROM app_user.dis_event_verify v JOIN app_user.dis_event e ON e.event_no = v.event_no WHERE v.is_real IS NOT NULL AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log l WHERE l.event_no = v.event_no AND l.oper_type IN (1, 2)); END IF; -- 2.3 电话转接 / 上报报送 IF to_regclass('app_user.dis_event_transfer_log') IS NOT NULL THEN INSERT INTO app_user.dis_event_log (event_id, event_no, oper_type, oper_desc, targets, oper_time) SELECT e.id, l.event_no, CASE WHEN l.action_type = '报送' THEN 4 ELSE 3 END, COALESCE(l.content, '转接报送(历史数据迁移)'), l.action_target, l.action_time FROM app_user.dis_event_transfer_log l JOIN app_user.dis_event e ON e.event_no = l.event_no WHERE l.event_no IS NOT NULL AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x WHERE x.event_no = l.event_no AND x.oper_type IN (3, 4) AND COALESCE(x.oper_time, TIMESTAMP '1970-01-01') = l.action_time); END IF; -- 2.4 处置跟踪 IF to_regclass('app_user.dis_event_track_log') IS NOT NULL THEN INSERT INTO app_user.dis_event_log (event_id, event_no, oper_type, oper_desc, trend, need_upgrade, oper_time) SELECT e.id, l.event_no, 6, COALESCE(l.content, '处置跟踪(历史数据迁移)'), l.assessment, CASE WHEN l.need_upgrade = '是' THEN 1 ELSE 0 END, l.action_time FROM app_user.dis_event_track_log l JOIN app_user.dis_event e ON e.event_no = l.event_no WHERE l.event_no IS NOT NULL AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x WHERE x.event_no = l.event_no AND x.oper_type = 6 AND COALESCE(x.oper_time, TIMESTAMP '1970-01-01') = l.action_time); END IF; -- 2.5 工作催办 IF to_regclass('app_user.dis_event_urge_detail') IS NOT NULL AND to_regclass('app_user.dis_event_urge') IS NOT NULL THEN INSERT INTO app_user.dis_event_log (event_id, event_no, oper_type, oper_desc, targets, receiver_dept, receiver_phone, method, urge_result, oper_time) SELECT e.id, d.event_no, 8, COALESCE(d.content, '工作催办(历史数据迁移)'), u.receiver, u.receiver_dept, u.receiver_phone, d.method, CASE d.result WHEN '已反馈' THEN '1' WHEN '未反馈' THEN '0' ELSE NULL END, d.urge_time FROM app_user.dis_event_urge_detail d JOIN app_user.dis_event_urge u ON u.id = d.urge_id JOIN app_user.dis_event e ON e.event_no = d.event_no WHERE d.event_no IS NOT NULL AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x WHERE x.event_no = d.event_no AND x.oper_type = 8 AND COALESCE(x.oper_time, TIMESTAMP '1970-01-01') = d.urge_time); END IF; -- 2.6 由状态轨迹推导「开始处置」 -- 旧模型没有「开始处置时间」列,取最早一条处置跟踪日志的时间作代理; -- 无跟踪日志时回退到解除时间 / 接报时间。 IF to_regclass('app_user.dis_event_track') IS NOT NULL AND to_regclass('app_user.dis_event_track_log') IS NOT NULL THEN INSERT INTO app_user.dis_event_log (event_id, event_no, oper_type, oper_desc, status_from, status_to, oper_time) SELECT e.id, e.event_no, 5, '开始处置(历史数据迁移)', 1, 2, COALESCE(m.first_track, e.release_time, e.report_time) FROM app_user.dis_event e LEFT JOIN ( SELECT t.rel_event_id, MIN(l.action_time) AS first_track FROM app_user.dis_event_track t JOIN app_user.dis_event_track_log l ON l.track_id = t.id GROUP BY t.rel_event_id ) m ON m.rel_event_id = e.id WHERE e.status IN (2, 3) AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x WHERE x.event_id = e.id AND x.oper_type = 5); END IF; -- 2.7 由状态轨迹推导「事件解除」 INSERT INTO app_user.dis_event_log (event_id, event_no, oper_type, oper_desc, status_from, status_to, oper_time) SELECT e.id, e.event_no, 7, COALESCE(e.release_note, '事件解除(历史数据迁移)'), 2, 3, e.release_time FROM app_user.dis_event e WHERE e.status = 3 AND e.release_time IS NOT NULL AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x WHERE x.event_id = e.id AND x.oper_type = 7); END $$; -- 分段提交:第二段成功即落库。 COMMIT; BEGIN; -- ===================================================================== -- 三、预案域:dis_event_plan + dis_plan_approval → dis_plan + dis_plan_version -- ===================================================================== DO $$ BEGIN IF to_regclass('app_user.dis_event_plan') IS NOT NULL THEN -- 3.1 主表搬迁;plan_no 用业务序号格式回填(YA + yyyyMMdd + 4 位补零序列) INSERT INTO app_user.dis_plan (plan_no, plan_name, plan_type, plan_level, compile_unit, publish_unit, publish_date, attach_name, attach_path, is_template, status, version, content, create_by, create_time) -- 拼接必须用 concat():本实例 `||` 是 boolean OR,详见文件头「方言红线」 SELECT concat('YA', to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), lpad(CAST(t.rn AS varchar), 4, '0')), t.plan_name, t.plan_type, t.plan_level, t.compile_unit, t.publish_unit, t.publish_date, t.attach_name, t.attach_path, 0, 0, t.version, t.plan_content, t.create_by, t.create_time FROM (SELECT p.*, row_number() OVER (ORDER BY p.id) AS rn FROM app_user.dis_event_plan p WHERE NOT EXISTS (SELECT 1 FROM app_user.dis_plan d WHERE d.plan_name = p.plan_name)) t; -- 3.2 已有正文且带版本号的历史预案,补一条「当前生效」版本快照 INSERT INTO app_user.dis_plan_version (plan_id, plan_no, version, content, change_note, editor, edit_time, ver_status) SELECT d.id, d.plan_no, COALESCE(NULLIF(d.version, ''), 'v1.0'), d.content, '历史数据迁移', d.create_by, d.create_time, 1 FROM app_user.dis_plan d WHERE d.status = 2 AND NOT EXISTS (SELECT 1 FROM app_user.dis_plan_version v WHERE v.plan_id = d.id); END IF; -- 3.3 审批表状态合并:编辑中/审批中/终审中/已发布/待修正 → 0/1/1/2/3 IF to_regclass('app_user.dis_plan_approval') IS NOT NULL THEN UPDATE app_user.dis_plan d SET status = CASE ap.status WHEN '已发布' THEN 2 WHEN '审批中' THEN 1 WHEN '终审中' THEN 1 WHEN '待修正' THEN 3 ELSE d.status END, approve_time = ap.approve_time, approve_note = COALESCE(d.approve_note, ap.audit_record), approve_result = CASE ap.status WHEN '已发布' THEN '通过' WHEN '待修正' THEN '不通过' ELSE d.approve_result END FROM app_user.dis_plan_approval ap WHERE d.plan_name = ap.plan_name AND d.status = 0; -- 3.4 审批轨迹补预案日志 INSERT INTO app_user.dis_plan_log (plan_id, plan_no, oper_type, oper_desc, version, status_to, oper_user_name, oper_time) SELECT d.id, d.plan_no, CASE ap.status WHEN '已发布' THEN 2 WHEN '待修正' THEN 3 ELSE 1 END, COALESCE(ap.audit_record, '历史审批记录迁移'), ap.version, CASE ap.status WHEN '已发布' THEN 2 WHEN '待修正' THEN 3 ELSE 1 END, ap.reviewers, ap.approve_time FROM app_user.dis_plan_approval ap JOIN app_user.dis_plan d ON d.plan_name = ap.plan_name WHERE NOT EXISTS (SELECT 1 FROM app_user.dis_plan_log l WHERE l.plan_id = d.id); END IF; -- 3.5 预案等级旧 4 级口径 → 新 3 级口径(执行前请按业务确认) UPDATE app_user.dis_plan SET plan_level = '1' WHERE plan_level IN ('一级', 'Ⅰ级', '特别重大', '重大', '紧急'); UPDATE app_user.dis_plan SET plan_level = '2' WHERE plan_level IN ('二级', 'Ⅱ级', '较大'); UPDATE app_user.dis_plan SET plan_level = '3' WHERE plan_level IN ('三级', '四级', 'Ⅲ级', 'Ⅳ级', '一般'); -- 3.6 预案类型中文 → 字典编码 UPDATE app_user.dis_plan SET plan_type = '1' WHERE plan_type = '综合'; UPDATE app_user.dis_plan SET plan_type = '2' WHERE plan_type = '专项'; UPDATE app_user.dis_plan SET plan_type = '3' WHERE plan_type = '现场处置'; END $$; -- 分段提交:第三段成功即落库。 COMMIT; BEGIN; -- ===================================================================== -- 四、事件分级:状态改数字 + 补业务编号 -- ===================================================================== DO $$ BEGIN -- 4.1 先把旧列 category / level 搬到新列(必须早于下面的口径映射) IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade' AND column_name = 'category') THEN EXECUTE 'UPDATE app_user.dis_event_grade SET event_category = category WHERE event_category IS NULL AND category IS NOT NULL'; END IF; IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade' AND column_name = 'level') THEN EXECUTE 'UPDATE app_user.dis_event_grade SET event_level = "level" WHERE event_level IS NULL AND "level" IS NOT NULL'; END IF; -- 4.2 状态下沉:启用/停用 → 0/1 UPDATE app_user.dis_event_grade SET status = '0' WHERE status::text IN ('启用', '正常'); UPDATE app_user.dis_event_grade SET status = '1' WHERE status::text IN ('停用', '禁用'); UPDATE app_user.dis_event_grade SET status = '0' WHERE status::text !~ '^[0-9]+$'; IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade' AND column_name = 'status' AND data_type <> 'smallint') THEN EXECUTE 'ALTER TABLE app_user.dis_event_grade ALTER COLUMN status TYPE int2 USING status::int2'; END IF; -- 4.3 等级口径:旧 4 级 → 新 3 级 UPDATE app_user.dis_event_grade SET event_level = '1' WHERE event_level IN ('一级', 'Ⅰ级', '特别重大', '重大', '紧急'); UPDATE app_user.dis_event_grade SET event_level = '2' WHERE event_level IN ('二级', 'Ⅱ级', '较大'); UPDATE app_user.dis_event_grade SET event_level = '3' WHERE event_level IN ('三级', '四级', 'Ⅲ级', 'Ⅳ级', '一般'); -- 4.4 类别口径 UPDATE app_user.dis_event_grade SET event_category = '1' WHERE event_category IN ('管网泄漏', '管道泄漏'); UPDATE app_user.dis_event_grade SET event_category = '2' WHERE event_category IN ('路面积水', '积水'); UPDATE app_user.dis_event_grade SET event_category = '3' WHERE event_category IN ('设施损坏', '塌陷'); UPDATE app_user.dis_event_grade SET event_category = '4' WHERE event_category IN ('管道堵塞', '堵塞'); -- 4.5 业务编号回填:GJ + yyyyMMdd + 3 位序号(按 id 顺序) -- 防御一:只补 grade_no IS NULL 的行,可重复执行。 -- 防御二:运行期取号(DispatchNumberSupport.nextGradeNo = GJ+yyyyMMdd+3位随机) -- 可能已经占用了同样的编号,这里逐个比对,撞号的候选值直接跳过 -- (该行保持 NULL,由页面人工补号),避免 uk_dis_event_grade_no 报重复键。 -- 注意:CURRENT_TIMESTAMP 在单个事务内是常量,同批回填的日期段一致。 -- 注意:拼接必须用 concat()。本实例 `||` 被解析为 boolean OR, -- 写成 'GJ' || to_char(...) || lpad(...) 会让整列都变成 'true'(详见文件头)。 UPDATE app_user.dis_event_grade g SET grade_no = c.new_no FROM (SELECT t.id, concat('GJ', to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), lpad(CAST(t.rn AS varchar), 3, '0')) AS new_no FROM (SELECT id, row_number() OVER (ORDER BY id) AS rn FROM app_user.dis_event_grade WHERE grade_no IS NULL) t) c WHERE g.id = c.id AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_grade x WHERE x.grade_no = c.new_no); END $$; COMMIT; -- ===================================================================== -- 五、迁移结果自检 -- ===================================================================== -- 1) 事件状态分布:只应是 0~4 -- SELECT status, count(*) FROM app_user.dis_event GROUP BY status ORDER BY status; -- 2) 事件主表编号唯一性 -- SELECT event_no, count(*) FROM app_user.dis_event GROUP BY event_no HAVING count(*) > 1; -- 3) 日志迁移覆盖率:有事件但无日志的异常数据 -- SELECT e.id, e.event_no FROM app_user.dis_event e -- WHERE NOT EXISTS (SELECT 1 FROM app_user.dis_event_log l WHERE l.event_id = e.id); -- 4) 各操作类型日志条数 -- SELECT oper_type, count(*) FROM app_user.dis_event_log GROUP BY oper_type ORDER BY oper_type; -- 5) 预案:有主表但无版本快照的已通过预案 -- SELECT d.id, d.plan_no, d.status FROM app_user.dis_plan d -- WHERE d.status = 2 AND NOT EXISTS (SELECT 1 FROM app_user.dis_plan_version v WHERE v.plan_id = d.id); -- 6) 分级标准编号回填情况 -- SELECT count(*) AS total, count(grade_no) AS with_no FROM app_user.dis_event_grade;