| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- -- 排水「所属片区」字典(KingbaseES)
- -- 用途:排水子系统里凡是出现「所属区域 / 所属片区」的下拉框,统一读取本字典,
- -- 不再从业务表的存量值里去重生成选项(旧做法会把脏数据回灌到下拉里)。
- -- 口径:与既有 DistrictEnum(com.zksy.base.enums.DistrictEnum)、
- -- equipment_base.district 保持一致:0-城南 / 1-城北 / 2-太常。
- -- 库中存编码,中文只在前端展示。
- -- 影响:drainage_waterlogging_point.district(varchar)由「中文地名」改为「编码」。
- -- 说明:脚本幂等,可重复执行;只维护字典 + 归一存量片区值,不改表结构。
- --
- -- =====================================================================
- -- 为什么本脚本要「显式指定主键」
- -- ---------------------------------------------------------------------
- -- 本库由 MySQL 迁移至 Kingbase,sys_dict_data / sys_dict_type 建表沿用了 MySQL 的
- -- auto_increment(KES 的 MySQL 兼容自增,不是 PG 的 serial/identity)。迁数据时是带显式
- -- 主键写入的,自增计数器没有跟着推进,于是「INSERT 不写主键」会发出一个已被占用的号:
- -- ERROR: 重复键违反唯一约束 "sys_dict_data_pkey"
- -- DETAIL: 键值 "dict_code"=(415) 已经存在
- -- 且该列不是 identity 列,pg_get_serial_sequence / pg_depend 都拿不到序列,
- -- ALTER COLUMN ... RESTART WITH 也会报「不是标识列」。
- --
- -- 因此处理方式是:
- -- ① INSERT 一律写成 max(主键) + n,显式指定,彻底绕开自增计数器;
- -- ② 插入完再把能找到的序列 setval 到最新 max(步骤 5),让后续新增不受影响;
- -- 若确实没有关联序列(纯 auto_increment),步骤 5 只提示、不报错。
- -- =====================================================================
- --
- -- 前置(必须先执行):结束上一次失败执行遗留的 aborted 事务
- -- 上一次语句报错后,PG/KES 会把该事务标记为已失败,此时同一连接上后续任何语句都只返回
- -- 「当前事务被终止,事务块结束之前的查询被忽略」,必须先回滚才能继续。
- -- 注意:本句会回滚该连接上所有未提交事务;若你有别的未提交手工操作,请先单独提交再跑本脚本。
- ROLLBACK;
- BEGIN;
- -- 1. 字典类型(幂等登记;dict_id 显式取 max+1)
- INSERT INTO app_user.sys_dict_type
- (dict_id, dict_name, dict_type, status, create_by, create_time, remark)
- SELECT (SELECT COALESCE(MAX(dict_id), 0) FROM app_user.sys_dict_type) + 1,
- '所属片区', 'drainage_district', '0', 'admin', CURRENT_TIMESTAMP,
- '排水所属片区:0城南 1城北 2太常(与 DistrictEnum 对齐)'
- WHERE NOT EXISTS (SELECT 1 FROM app_user.sys_dict_type WHERE dict_type = 'drainage_district');
- UPDATE app_user.sys_dict_type
- SET dict_name = '所属片区',
- status = '0',
- update_by = 'admin',
- update_time = CURRENT_TIMESTAMP,
- remark = '排水所属片区:0城南 1城北 2太常(与 DistrictEnum 对齐)'
- WHERE dict_type = 'drainage_district';
- -- 2. 字典数据(幂等重写;dict_code 显式取 max+序号)
- -- 片区不是严重性等级,list_class 统一留空走默认色,不做颜色区分。
- DELETE FROM app_user.sys_dict_data WHERE dict_type = 'drainage_district';
- INSERT INTO app_user.sys_dict_data
- (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class,
- is_default, status, create_by, create_time, remark)
- SELECT (SELECT COALESCE(MAX(dict_code), 0) FROM app_user.sys_dict_data) + v.rn,
- v.dict_sort, v.dict_label, v.dict_value, 'drainage_district',
- '', '', v.is_default, '0', 'admin', CURRENT_TIMESTAMP, v.remark
- FROM (VALUES
- (1, 1, '城南', '0', 'N', '所属片区:城南'),
- (2, 2, '城北', '1', 'N', '所属片区:城北'),
- (3, 3, '太常', '2', 'N', '所属片区:太常')
- ) AS v(rn, dict_sort, dict_label, dict_value, is_default, remark)
- ORDER BY v.rn;
- -- 3. 列注释对齐(幂等,可重复执行)
- COMMENT ON COLUMN app_user.drainage_waterlogging_point.district
- IS '所属片区(字典 drainage_district:0城南 1城北 2太常)';
- -- 4. 存量片区值归一:中文地名 → 编码
- -- 只处理能精确匹配的三个片区;其余值(历史脏数据,如乱填的 '123123')
- -- 一律原样保留,交由步骤 6 的体检输出,由人工确认后再处理,避免误伤真实数据。
- UPDATE app_user.drainage_waterlogging_point
- SET district = CASE TRIM(district)
- WHEN '城南' THEN '0'
- WHEN '城北' THEN '1'
- WHEN '太常' THEN '2'
- END
- WHERE TRIM(district) IN ('城南', '城北', '太常');
- -- 5. 自增计数器校准(放在插入之后:此时 max 已含新插入的 3 行)
- -- 三种方式依次尝试,任一成功即可;都不成功只提示,不影响本脚本已插入的数据。
- -- 方式一 pg_get_serial_sequence(serial / identity)
- -- 方式二 从列默认值里解析 nextval('...')(KES 常见形态)
- -- 方式三 pg_depend 关联序列(兜底)
- DO $$
- DECLARE
- t record;
- v_seq text;
- v_max bigint;
- v_default text;
- BEGIN
- FOR t IN
- SELECT * FROM (VALUES ('sys_dict_data', 'dict_code'),
- ('sys_dict_type', 'dict_id')) AS x(tbl, col)
- LOOP
- v_seq := NULL;
- v_default := NULL;
- -- 方式一
- BEGIN
- v_seq := pg_get_serial_sequence(format('app_user.%I', t.tbl), t.col);
- EXCEPTION WHEN OTHERS THEN
- v_seq := NULL;
- END;
- -- 方式二:解析列默认值里的 nextval('xxx')
- IF v_seq IS NULL THEN
- BEGIN
- SELECT pg_get_expr(ad.adbin, ad.adrelid) INTO v_default
- FROM pg_attribute a
- JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum
- WHERE a.attrelid = format('app_user.%I', t.tbl)::regclass
- AND a.attname::text = t.col
- AND a.attnum > 0
- AND NOT a.attisdropped;
- EXCEPTION WHEN OTHERS THEN
- v_default := NULL;
- END;
- IF v_default LIKE '%nextval%' THEN
- v_seq := substring(v_default from 'nextval\(''([^'']+)''');
- END IF;
- END IF;
- -- 方式三
- IF v_seq IS NULL THEN
- BEGIN
- SELECT d.objid::regclass::text INTO v_seq
- FROM pg_depend d
- JOIN pg_attribute a ON a.attrelid = d.refobjid AND a.attnum = d.refobjsubid
- JOIN pg_class sc ON sc.oid = d.objid AND sc.relkind = 'S'
- WHERE d.refobjid = format('app_user.%I', t.tbl)::regclass
- AND a.attname::text = t.col
- AND d.deptype IN ('a', 'i')
- LIMIT 1;
- EXCEPTION WHEN OTHERS THEN
- v_seq := NULL;
- END;
- END IF;
- IF v_seq IS NULL THEN
- RAISE NOTICE '%.% 没有关联序列(应为 MySQL 兼容 auto_increment),跳过校准。本次数据已显式指定主键插入,不影响本脚本;但后续在该表手工新增行仍可能撞主键。',
- t.tbl, t.col;
- CONTINUE;
- END IF;
- EXECUTE format('SELECT COALESCE(max(%I), 0) FROM app_user.%I', t.col, t.tbl) INTO v_max;
- BEGIN
- PERFORM setval(v_seq::regclass, GREATEST(v_max, 1), true);
- RAISE NOTICE '%.% 自增序列已校准: % (max=%, next=%)', t.tbl, t.col, v_seq, v_max, v_max + 1;
- EXCEPTION WHEN OTHERS THEN
- RAISE NOTICE '%.% 序列校准失败: %,跳过', t.tbl, t.col, SQLERRM;
- END;
- END LOOP;
- END $$;
- -- 6. 存量数据体检(只提示不改数据):片区值不在 0/1/2 的记录
- -- 出现提示说明该行是历史脏数据,前端下拉已由字典接管不会再产生新脏值,
- -- 存量部分需要人工判定归属后单独 UPDATE。
- DO $$
- DECLARE
- r record;
- v_rows bigint := 0;
- BEGIN
- FOR r IN
- SELECT district, COUNT(*) AS cnt
- FROM app_user.drainage_waterlogging_point
- WHERE del_flag = '0'
- AND (district IS NULL OR TRIM(district) = '' OR TRIM(district) NOT IN ('0', '1', '2'))
- GROUP BY district
- ORDER BY COUNT(*) DESC
- LOOP
- IF r.district IS NULL OR TRIM(r.district) = '' THEN
- RAISE NOTICE '片区待核对: (空值) —— % 条', r.cnt;
- ELSE
- RAISE NOTICE '片区待核对: % —— % 条', r.district, r.cnt;
- END IF;
- v_rows := v_rows + r.cnt;
- END LOOP;
- IF v_rows = 0 THEN
- RAISE NOTICE '片区值全部合法(0/1/2),无需处理。';
- ELSE
- RAISE NOTICE '共 % 条记录片区值不合法,请人工核对后处理。', v_rows;
- END IF;
- END $$;
- -- 7. 结果核验(可选,取消注释执行):应返回 3 行,dict_value 依次 0/1/2
- -- SELECT dict_value, dict_label FROM app_user.sys_dict_data
- -- WHERE dict_type = 'drainage_district' ORDER BY dict_sort;
- COMMIT;
|