| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- -- ============================================================================
- -- 排水基础数据:一次性补齐脚本(KingbaseES, schema app_user)
- --
- -- 适用场景
- -- 排水子系统页面报「字段 xxx 不存在」,例如:
- -- ERROR: 字段 "duty_dept_id" 不存在
- -- ERROR: 关系 "early_warning" 的 "longitude" 字段不存在
- -- 说明库里的表还停留在旧版本。本脚本把本轮所有相关改动合并成一次执行,
- -- 不改表结构之外的东西,不删任何数据。
- --
- -- 包含四段(均可单独复制执行)
- -- 1. drainage_waterlogging_point:补齐实体用到的全部列 + 索引
- -- 2. 责任单位/责任人:duty_dept_id / duty_user_id 的存量回填(只做精确匹配)
- -- 3. 字典 drainage_district:0城南 1城北 2太常
- -- 4. early_warning:device_name 补齐、warning_level 转 integer、
- -- 接收人表 psgw_warning_scope_user 建表、旧版遗留列放开非空
- --
- -- 幂等:可重复执行,已到位的部分只会输出 NOTICE。
- -- ============================================================================
- -- 结束上一次失败执行遗留的 aborted 事务(在事务外执行只会告警,不报错)
- ROLLBACK;
- BEGIN;
- SET search_path TO app_user, public;
- -- ============================================================================
- -- 1. 易积水点表:补齐实体所需的全部列
- -- (老库可能只缺 duty_* 两列,也可能缺 water_depth / risk_level 等,
- -- 这里按实体字段全集逐列判断,缺哪列补哪列)
- -- ============================================================================
- DO $$
- DECLARE
- col record;
- added int := 0;
- BEGIN
- IF to_regclass('app_user.drainage_waterlogging_point') IS NULL THEN
- RAISE NOTICE '1. 表 app_user.drainage_waterlogging_point 不存在,跳过补列';
- RETURN;
- END IF;
- FOR col IN
- SELECT * FROM (VALUES
- ('point_code', 'varchar(64)'),
- ('point_name', 'varchar(200)'),
- ('district', 'varchar(100)'),
- ('location', 'varchar(500)'),
- ('longitude', 'numeric(12,8)'),
- ('latitude', 'numeric(12,8)'),
- ('waterlogging_level', 'int2'),
- ('water_depth', 'numeric(10,2)'),
- ('water_volume', 'numeric(14,2)'),
- ('water_area', 'numeric(14,2)'),
- ('risk_level', 'varchar(8)'),
- ('duty_unit', 'varchar(200)'),
- ('duty_person', 'varchar(64)'),
- ('duty_phone', 'varchar(32)'),
- ('duty_dept_id', 'bigint'),
- ('duty_user_id', 'bigint'),
- ('camera_ids', 'varchar(500)'),
- ('remark', 'varchar(500)'),
- ('create_by', 'varchar(64)'),
- ('create_time', 'timestamp'),
- ('update_by', 'varchar(64)'),
- ('update_time', 'timestamp'),
- ('del_flag', 'char(1)')
- ) AS x(name, type)
- LOOP
- IF NOT EXISTS (
- SELECT 1 FROM information_schema.columns
- WHERE table_schema = 'app_user'
- AND table_name = 'drainage_waterlogging_point'
- AND column_name = col.name
- ) THEN
- EXECUTE format('ALTER TABLE app_user.drainage_waterlogging_point ADD COLUMN %I %s',
- col.name, col.type);
- added := added + 1;
- RAISE NOTICE '1. 已补列:drainage_waterlogging_point.%', col.name;
- END IF;
- END LOOP;
- IF added = 0 THEN
- RAISE NOTICE '1. drainage_waterlogging_point 列已齐全,无需改动';
- END IF;
- END $$;
- -- del_flag 有历史 NULL 值时逻辑删除过滤会漏行,统一补成 '0'
- DO $$
- BEGIN
- IF to_regclass('app_user.drainage_waterlogging_point') IS NULL THEN
- RAISE NOTICE '1. 表不存在,跳过索引与注释';
- RETURN;
- END IF;
- UPDATE app_user.drainage_waterlogging_point SET del_flag = '0' WHERE del_flag IS NULL;
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_dwp_point_code ON app_user.drainage_waterlogging_point (point_code)';
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_dwp_point_name ON app_user.drainage_waterlogging_point (point_name)';
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_dwp_district ON app_user.drainage_waterlogging_point (district)';
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_dwp_level ON app_user.drainage_waterlogging_point (waterlogging_level)';
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_dwp_risk_level ON app_user.drainage_waterlogging_point (risk_level)';
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_dwp_duty_dept ON app_user.drainage_waterlogging_point (duty_dept_id)';
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_dwp_duty_user ON app_user.drainage_waterlogging_point (duty_user_id)';
- EXECUTE 'COMMENT ON COLUMN app_user.drainage_waterlogging_point.district IS ''所属片区(字典 drainage_district:0城南 1城北 2太常)''';
- EXECUTE 'COMMENT ON COLUMN app_user.drainage_waterlogging_point.duty_unit IS ''责任单位(名称快照,对应 duty_dept_id)''';
- EXECUTE 'COMMENT ON COLUMN app_user.drainage_waterlogging_point.duty_person IS ''责任人(姓名快照,对应 duty_user_id)''';
- EXECUTE 'COMMENT ON COLUMN app_user.drainage_waterlogging_point.duty_phone IS ''联系电话(快照,选人时取 sys_user.phonenumber)''';
- EXECUTE 'COMMENT ON COLUMN app_user.drainage_waterlogging_point.duty_dept_id IS ''责任单位ID(sys_dept.dept_id)''';
- EXECUTE 'COMMENT ON COLUMN app_user.drainage_waterlogging_point.duty_user_id IS ''责任人ID(sys_user.user_id)''';
- END $$;
- -- ============================================================================
- -- 2. 责任单位/责任人回填:按名称精确匹配,匹配不上保持 NULL(不猜)
- -- 同一名称对应多条时取 ID 最小的一个,避免一对多歧义放大。
- -- ============================================================================
- DO $$
- DECLARE
- v_dept integer := 0;
- v_user integer := 0;
- BEGIN
- IF to_regclass('app_user.drainage_waterlogging_point') IS NULL THEN
- RAISE NOTICE '2. 表不存在,跳过回填';
- RETURN;
- END IF;
- UPDATE app_user.drainage_waterlogging_point p
- SET duty_dept_id = d.dept_id
- FROM (SELECT dept_name, MIN(dept_id) AS dept_id
- FROM app_user.sys_dept
- WHERE del_flag = '0'
- GROUP BY dept_name) d
- WHERE p.duty_dept_id IS NULL
- AND p.duty_unit IS NOT NULL
- AND trim(p.duty_unit) = d.dept_name;
- GET DIAGNOSTICS v_dept = ROW_COUNT;
- UPDATE app_user.drainage_waterlogging_point p
- SET duty_user_id = u.user_id
- FROM (SELECT nick_name, MIN(user_id) AS user_id
- FROM app_user.sys_user
- WHERE del_flag = '0'
- GROUP BY nick_name) u
- WHERE p.duty_user_id IS NULL
- AND p.duty_person IS NOT NULL
- AND trim(p.duty_person) = u.nick_name;
- GET DIAGNOSTICS v_user = ROW_COUNT;
- RAISE NOTICE '2. 存量回填:责任单位匹配 % 条,责任人匹配 % 条', v_dept, v_user;
- END $$;
- -- ============================================================================
- -- 3. 字典:所属片区 drainage_district
- -- 本库 sys_dict_data / sys_dict_type 是 MySQL 兼容 auto_increment,
- -- 自增计数器未随迁移数据推进,主键一律显式取 max+序号。
- -- ============================================================================
- 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';
- 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;
- -- 存量片区值归一:中文地名 → 编码(无法识别的一律原样保留,避免误伤脏数据)
- DO $$
- BEGIN
- IF to_regclass('app_user.drainage_waterlogging_point') IS NULL THEN
- RAISE NOTICE '3. 表不存在,跳过片区值归一';
- RETURN;
- END IF;
- 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 ('城南', '城北', '太常');
- END $$;
- -- 字典自增序列尽力校准(三种方式兜底,失败只提示)
- 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;
- 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 '3. %.% 没有关联序列(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 '3. %.% 自增序列已校准 (max=%)', t.tbl, t.col, v_max;
- EXCEPTION WHEN OTHERS THEN
- RAISE NOTICE '3. %.% 序列校准失败: %,跳过', t.tbl, t.col, SQLERRM;
- END;
- END LOOP;
- END $$;
- -- ============================================================================
- -- 4. 预警表 early_warning
- -- ============================================================================
- DO $$
- DECLARE
- has_device_name boolean;
- has_warning_name boolean;
- v_type text;
- col_name text;
- exists_col boolean;
- BEGIN
- IF to_regclass('app_user.early_warning') IS NULL THEN
- RAISE NOTICE '4. 表 app_user.early_warning 不存在,跳过';
- RETURN;
- END IF;
- -- 4.1 设备名称列:实体映射 device_name
- SELECT EXISTS (SELECT 1 FROM information_schema.columns
- WHERE table_schema = 'app_user' AND table_name = 'early_warning'
- AND column_name = 'device_name') INTO has_device_name;
- SELECT EXISTS (SELECT 1 FROM information_schema.columns
- WHERE table_schema = 'app_user' AND table_name = 'early_warning'
- AND column_name = 'warning_name') INTO has_warning_name;
- IF has_device_name THEN
- RAISE NOTICE '4.1 early_warning.device_name 已存在';
- ELSE
- ALTER TABLE app_user.early_warning ADD COLUMN device_name varchar(256);
- IF has_warning_name THEN
- EXECUTE 'UPDATE app_user.early_warning SET device_name = warning_name WHERE device_name IS NULL';
- RAISE NOTICE '4.1 已新增 device_name,并搬迁 warning_name 的数据';
- ELSE
- RAISE NOTICE '4.1 已新增 device_name';
- END IF;
- END IF;
- -- 4.2 warning_level 统一为 integer(实体/查询/前端都是整数)
- SELECT data_type INTO v_type FROM information_schema.columns
- WHERE table_schema = 'app_user' AND table_name = 'early_warning' AND column_name = 'warning_level';
- IF v_type IS NULL THEN
- ALTER TABLE app_user.early_warning ADD COLUMN warning_level integer;
- RAISE NOTICE '4.2 已新增 warning_level integer';
- ELSIF v_type IN ('integer', 'smallint', 'bigint', 'numeric', 'decimal') THEN
- RAISE NOTICE '4.2 warning_level 已是数值类型(%),跳过', v_type;
- ELSE
- ALTER TABLE app_user.early_warning ALTER COLUMN warning_level DROP DEFAULT;
- ALTER TABLE app_user.early_warning
- ALTER COLUMN warning_level TYPE integer
- USING (CASE WHEN trim(warning_level) ~ '^[0-9]+$' THEN trim(warning_level)::integer ELSE 1 END);
- RAISE NOTICE '4.2 warning_level 已由 % 转为 integer', v_type;
- END IF;
- -- 4.3 旧版实体遗留列:程序已不再写入,放开非空避免插入失败
- FOREACH col_name IN ARRAY ARRAY['current_value', 'threshold', 'unit', 'warning_time'] LOOP
- SELECT EXISTS (SELECT 1 FROM information_schema.columns
- WHERE table_schema = 'app_user' AND table_name = 'early_warning'
- AND column_name = col_name) INTO exists_col;
- IF exists_col THEN
- EXECUTE format('ALTER TABLE app_user.early_warning ALTER COLUMN %I DROP NOT NULL', col_name);
- RAISE NOTICE '4.3 early_warning.% 已改为可空', col_name;
- END IF;
- END LOOP;
- END $$;
- -- 4.4 指定人员发布的接收人表
- CREATE TABLE IF NOT EXISTS app_user.psgw_warning_scope_user (
- id varchar(64) NOT NULL,
- warning_id varchar(64),
- user_id bigint,
- user_name varchar(128),
- approved integer DEFAULT 0,
- approve_time timestamp,
- create_time timestamp,
- PRIMARY KEY (id)
- );
- CREATE INDEX IF NOT EXISTS idx_psgw_warning_scope_user_warning
- ON app_user.psgw_warning_scope_user (warning_id);
- COMMENT ON TABLE app_user.psgw_warning_scope_user IS '排水预警接收人员';
- COMMIT;
- -- ============================================================================
- -- 核验(可选,取消注释后单独执行)
- -- ============================================================================
- -- SELECT column_name, data_type FROM information_schema.columns
- -- WHERE table_schema = 'app_user' AND table_name = 'drainage_waterlogging_point'
- -- ORDER BY ordinal_position;
- --
- -- SELECT dict_value, dict_label FROM app_user.sys_dict_data
- -- WHERE dict_type = 'drainage_district' ORDER BY dict_sort;
|