-- ============================================ -- 数据模型统一重构脚本 -- 目标:统一vehicle_id为数字ID,license_plate为车牌号 -- 创建时间:2025-01-15 -- 执行模式:方案B - 完全统一数据模型 -- ============================================ -- 开始事务 BEGIN; -- 设置错误处理 \set ON_ERROR_STOP on -- ============================================ -- 第一阶段:数据结构准备 -- ============================================ -- 创建车辆ID映射表(临时) CREATE TABLE IF NOT EXISTS vehicle_id_mapping ( license_plate VARCHAR(50) PRIMARY KEY, old_vehicle_id VARCHAR(50), new_vehicle_id BIGINT ); -- 填充映射表数据 INSERT INTO vehicle_id_mapping (license_plate, old_vehicle_id, new_vehicle_id) SELECT license_plate_number as license_plate, NULL as old_vehicle_id, vehicle_id as new_vehicle_id FROM sys_vehicle_info ON CONFLICT (license_plate) DO NOTHING; -- 从CollisionAvoidanceSystem表中获取现有车牌号 INSERT INTO vehicle_id_mapping (license_plate, old_vehicle_id, new_vehicle_id) SELECT DISTINCT vehicle_id as license_plate, vehicle_id as old_vehicle_id, NULL::BIGINT as new_vehicle_id FROM vehicle_locations WHERE vehicle_id NOT IN (SELECT license_plate FROM vehicle_id_mapping) ON CONFLICT (license_plate) DO NOTHING; -- 为新车牌号分配数字ID DO $$ DECLARE max_id BIGINT; rec RECORD; BEGIN -- 获取当前最大车辆ID SELECT COALESCE(MAX(vehicle_id), 0) INTO max_id FROM sys_vehicle_info; -- 为没有数字ID的车牌号分配新ID FOR rec IN SELECT license_plate FROM vehicle_id_mapping WHERE new_vehicle_id IS NULL ORDER BY license_plate LOOP max_id := max_id + 1; UPDATE vehicle_id_mapping SET new_vehicle_id = max_id WHERE license_plate = rec.license_plate; END LOOP; END $$; -- ============================================ -- 第二阶段:QAUP-Management表结构变更 -- ============================================ -- 1. 重命名sys_vehicle_info表中的字段 ALTER TABLE sys_vehicle_info RENAME COLUMN license_plate_number TO license_plate; -- 2. 为新车牌号添加车辆记录 INSERT INTO sys_vehicle_info ( vehicle_id, license_plate, vin_number, type_id, brand, owning_unit, contact_person, phone_number, image_url, create_by, create_time, update_by, update_time, remark ) SELECT vim.new_vehicle_id, vim.license_plate, 'AUTO_' || vim.license_plate as vin_number, 1 as type_id, -- 默认类型,需要根据实际情况调整 '未知' as brand, '自动导入' as owning_unit, '系统' as contact_person, '' as phone_number, '' as image_url, 'system' as create_by, NOW() as create_time, 'system' as update_by, NOW() as update_time, '数据模型统一时自动创建' as remark FROM vehicle_id_mapping vim WHERE vim.new_vehicle_id NOT IN (SELECT vehicle_id FROM sys_vehicle_info) AND vim.old_vehicle_id IS NOT NULL; -- ============================================ -- 第三阶段:CollisionAvoidanceSystem表结构变更 -- ============================================ -- 1. 处理vehicle_locations表 -- 添加新字段 ALTER TABLE vehicle_locations ADD COLUMN IF NOT EXISTS license_plate VARCHAR(50); ALTER TABLE vehicle_locations ADD COLUMN IF NOT EXISTS vehicle_id_new BIGINT; -- 填充新字段数据 UPDATE vehicle_locations vl SET license_plate = vl.vehicle_id, vehicle_id_new = vim.new_vehicle_id FROM vehicle_id_mapping vim WHERE vl.vehicle_id = vim.license_plate; -- 2. 处理vehicle_trajectories表 -- 添加新字段 ALTER TABLE vehicle_trajectories ADD COLUMN IF NOT EXISTS license_plate VARCHAR(50); ALTER TABLE vehicle_trajectories ADD COLUMN IF NOT EXISTS vehicle_id_new BIGINT; -- 填充新字段数据 UPDATE vehicle_trajectories vt SET license_plate = vt.vehicle_id, vehicle_id_new = vim.new_vehicle_id FROM vehicle_id_mapping vim WHERE vt.vehicle_id = vim.license_plate; -- 3. 处理vehicle_commands表 -- 添加新字段 ALTER TABLE vehicle_commands ADD COLUMN IF NOT EXISTS license_plate VARCHAR(50); ALTER TABLE vehicle_commands ADD COLUMN IF NOT EXISTS vehicle_id_new BIGINT; -- 填充新字段数据 UPDATE vehicle_commands vc SET license_plate = vc.vehicle_id, vehicle_id_new = vim.new_vehicle_id FROM vehicle_id_mapping vim WHERE vc.vehicle_id = vim.license_plate; -- 4. 处理rule_violation_events表 -- 添加新字段 ALTER TABLE rule_violation_events ADD COLUMN IF NOT EXISTS license_plate VARCHAR(50); ALTER TABLE rule_violation_events ADD COLUMN IF NOT EXISTS vehicle_id_new BIGINT; -- 填充新字段数据(只处理车辆类型的事件) UPDATE rule_violation_events rve SET license_plate = rve.subject_id, vehicle_id_new = vim.new_vehicle_id FROM vehicle_id_mapping vim WHERE rve.subject_id = vim.license_plate AND rve.subject_type = 'VEHICLE'; -- ============================================ -- 第四阶段:数据完整性验证 -- ============================================ -- 验证数据完整性 DO $$ DECLARE missing_data_count INTEGER; total_records INTEGER; BEGIN -- 检查vehicle_locations表 SELECT COUNT(*) INTO missing_data_count FROM vehicle_locations WHERE license_plate IS NULL OR vehicle_id_new IS NULL; SELECT COUNT(*) INTO total_records FROM vehicle_locations; IF missing_data_count > 0 THEN RAISE EXCEPTION 'vehicle_locations表数据迁移不完整: %/%', missing_data_count, total_records; END IF; -- 检查vehicle_trajectories表 SELECT COUNT(*) INTO missing_data_count FROM vehicle_trajectories WHERE license_plate IS NULL OR vehicle_id_new IS NULL; SELECT COUNT(*) INTO total_records FROM vehicle_trajectories; IF missing_data_count > 0 THEN RAISE EXCEPTION 'vehicle_trajectories表数据迁移不完整: %/%', missing_data_count, total_records; END IF; -- 检查vehicle_commands表 SELECT COUNT(*) INTO missing_data_count FROM vehicle_commands WHERE license_plate IS NULL OR vehicle_id_new IS NULL; SELECT COUNT(*) INTO total_records FROM vehicle_commands; IF missing_data_count > 0 THEN RAISE EXCEPTION 'vehicle_commands表数据迁移不完整: %/%', missing_data_count, total_records; END IF; RAISE NOTICE '数据完整性验证通过!'; END $$; -- ============================================ -- 第五阶段:结构清理和字段重命名 -- ============================================ -- 1. 删除旧字段,重命名新字段 - vehicle_locations ALTER TABLE vehicle_locations DROP COLUMN vehicle_id; ALTER TABLE vehicle_locations RENAME COLUMN vehicle_id_new TO vehicle_id; -- 2. 删除旧字段,重命名新字段 - vehicle_trajectories ALTER TABLE vehicle_trajectories DROP COLUMN vehicle_id; ALTER TABLE vehicle_trajectories RENAME COLUMN vehicle_id_new TO vehicle_id; -- 3. 删除旧字段,重命名新字段 - vehicle_commands ALTER TABLE vehicle_commands DROP COLUMN vehicle_id; ALTER TABLE vehicle_commands RENAME COLUMN vehicle_id_new TO vehicle_id; -- 4. 删除旧字段,重命名新字段 - rule_violation_events -- 注意:这里需要谨慎处理,因为subject_id可能不只是车辆ID ALTER TABLE rule_violation_events ADD COLUMN IF NOT EXISTS subject_id_new VARCHAR(50); UPDATE rule_violation_events SET subject_id_new = CASE WHEN subject_type = 'VEHICLE' THEN vehicle_id_new::VARCHAR ELSE subject_id END; ALTER TABLE rule_violation_events DROP COLUMN subject_id; ALTER TABLE rule_violation_events RENAME COLUMN subject_id_new TO subject_id; -- ============================================ -- 第六阶段:重建索引和约束 -- ============================================ -- 1. vehicle_locations表索引 DROP INDEX IF EXISTS idx_vehicle_locations_vehicle_id; DROP INDEX IF EXISTS idx_vehicle_locations_license_plate; CREATE INDEX idx_vehicle_locations_vehicle_id ON vehicle_locations(vehicle_id); CREATE INDEX idx_vehicle_locations_license_plate ON vehicle_locations(license_plate); -- 2. vehicle_trajectories表索引 DROP INDEX IF EXISTS idx_vehicle_trajectories_vehicle_id; DROP INDEX IF EXISTS idx_vehicle_trajectories_license_plate; CREATE INDEX idx_vehicle_trajectories_vehicle_id ON vehicle_trajectories(vehicle_id); CREATE INDEX idx_vehicle_trajectories_license_plate ON vehicle_trajectories(license_plate); -- 3. vehicle_commands表索引 DROP INDEX IF EXISTS idx_vehicle_commands_vehicle_id; DROP INDEX IF EXISTS idx_vehicle_commands_license_plate; CREATE INDEX idx_vehicle_commands_vehicle_id ON vehicle_commands(vehicle_id); CREATE INDEX idx_vehicle_commands_license_plate ON vehicle_commands(license_plate); -- 4. rule_violation_events表索引 DROP INDEX IF EXISTS idx_rule_violations_license_plate; CREATE INDEX idx_rule_violations_license_plate ON rule_violation_events(license_plate) WHERE subject_type = 'VEHICLE'; -- ============================================ -- 第七阶段:添加外键约束 -- ============================================ -- 添加外键约束(可选,根据需要启用) -- ALTER TABLE vehicle_locations ADD CONSTRAINT fk_vehicle_locations_vehicle_id -- FOREIGN KEY (vehicle_id) REFERENCES sys_vehicle_info(vehicle_id); -- ALTER TABLE vehicle_trajectories ADD CONSTRAINT fk_vehicle_trajectories_vehicle_id -- FOREIGN KEY (vehicle_id) REFERENCES sys_vehicle_info(vehicle_id); -- ALTER TABLE vehicle_commands ADD CONSTRAINT fk_vehicle_commands_vehicle_id -- FOREIGN KEY (vehicle_id) REFERENCES sys_vehicle_info(vehicle_id); -- ============================================ -- 第八阶段:更新业务视图 -- ============================================ -- 删除旧视图 DROP VIEW IF EXISTS vehicle_complete_info; DROP VIEW IF EXISTS vehicle_status_summary; DROP VIEW IF EXISTS vehicle_info_with_location; DROP VIEW IF EXISTS vehicle_trajectory_view; -- 重新创建统一的业务视图 CREATE VIEW vehicle_complete_info AS SELECT vi.vehicle_id, vi.license_plate, vi.vin_number, vi.type_id, vt.type_name, vi.brand, vi.owning_unit, vi.contact_person, vi.phone_number, vi.image_url, vl.location, vl.altitude, vl.heading, vl.speed, vl.timestamp as last_location_time, vl.data_quality, vi.create_time, vi.update_time FROM sys_vehicle_info vi LEFT JOIN sys_vehicle_type vt ON vi.type_id = vt.type_id LEFT JOIN LATERAL ( SELECT * FROM vehicle_locations WHERE vehicle_id = vi.vehicle_id ORDER BY timestamp DESC LIMIT 1 ) vl ON true; -- 车辆状态统计视图 CREATE VIEW vehicle_status_summary AS SELECT vehicle_type, COUNT(*) as total_vehicles, COUNT(CASE WHEN timestamp > NOW() - INTERVAL '5 minutes' THEN 1 END) as active_vehicles, COUNT(CASE WHEN timestamp <= NOW() - INTERVAL '5 minutes' THEN 1 END) as inactive_vehicles, AVG(speed) as avg_speed, MAX(speed) as max_speed FROM vehicle_locations vl WHERE timestamp > NOW() - INTERVAL '24 hours' GROUP BY vehicle_type; -- 车辆信息与位置关联视图 CREATE VIEW vehicle_info_with_location AS SELECT vi.vehicle_id, vi.license_plate, vi.brand, vi.owning_unit, vt.type_name as vehicle_type_name, vl.vehicle_type as location_vehicle_type, vl.location, ST_X(vl.location) as longitude, ST_Y(vl.location) as latitude, vl.altitude, vl.heading, vl.speed, vl.timestamp as last_update_time, CASE WHEN vl.timestamp > NOW() - INTERVAL '5 minutes' THEN 'ACTIVE' WHEN vl.timestamp > NOW() - INTERVAL '30 minutes' THEN 'INACTIVE' ELSE 'OFFLINE' END as status FROM sys_vehicle_info vi LEFT JOIN sys_vehicle_type vt ON vi.type_id = vt.type_id LEFT JOIN LATERAL ( SELECT * FROM vehicle_locations WHERE vehicle_id = vi.vehicle_id ORDER BY timestamp DESC LIMIT 1 ) vl ON true; -- 车辆轨迹查询视图 CREATE VIEW vehicle_trajectory_view AS SELECT vi.vehicle_id, vi.license_plate, vi.brand, vt.trajectory_date, vt.trajectory_line, vt.total_distance, vt.max_speed, vt.avg_speed, vt.duration_seconds, vt.start_time, vt.end_time FROM sys_vehicle_info vi INNER JOIN vehicle_trajectories vt ON vt.vehicle_id = vi.vehicle_id LEFT JOIN sys_vehicle_type vtype ON vi.type_id = vtype.type_id; -- ============================================ -- 第九阶段:添加字段注释 -- ============================================ -- sys_vehicle_info表字段注释 COMMENT ON COLUMN sys_vehicle_info.license_plate IS '车牌号(统一字段名)'; -- vehicle_locations表字段注释 COMMENT ON COLUMN vehicle_locations.vehicle_id IS '车辆ID(数字主键,关联sys_vehicle_info.vehicle_id)'; COMMENT ON COLUMN vehicle_locations.license_plate IS '车牌号(业务标识符)'; -- vehicle_trajectories表字段注释 COMMENT ON COLUMN vehicle_trajectories.vehicle_id IS '车辆ID(数字主键,关联sys_vehicle_info.vehicle_id)'; COMMENT ON COLUMN vehicle_trajectories.license_plate IS '车牌号(业务标识符)'; -- vehicle_commands表字段注释 COMMENT ON COLUMN vehicle_commands.vehicle_id IS '车辆ID(数字主键,关联sys_vehicle_info.vehicle_id)'; COMMENT ON COLUMN vehicle_commands.license_plate IS '车牌号(业务标识符)'; -- rule_violation_events表字段注释 COMMENT ON COLUMN rule_violation_events.license_plate IS '车牌号(仅当subject_type为VEHICLE时有效)'; -- ============================================ -- 第十阶段:清理临时数据 -- ============================================ -- 删除临时映射表 DROP TABLE IF EXISTS vehicle_id_mapping; -- ============================================ -- 最终验证 -- ============================================ DO $$ DECLARE qaup_count INTEGER; locations_count INTEGER; trajectories_count INTEGER; commands_count INTEGER; violations_count INTEGER; BEGIN -- 统计各表记录数 SELECT COUNT(*) INTO qaup_count FROM sys_vehicle_info; SELECT COUNT(*) INTO locations_count FROM vehicle_locations; SELECT COUNT(*) INTO trajectories_count FROM vehicle_trajectories; SELECT COUNT(*) INTO commands_count FROM vehicle_commands; SELECT COUNT(*) INTO violations_count FROM rule_violation_events WHERE subject_type = 'VEHICLE'; -- 输出统计信息 RAISE NOTICE '=============================================='; RAISE NOTICE '数据模型统一重构完成!'; RAISE NOTICE '=============================================='; RAISE NOTICE '车辆信息记录: %', qaup_count; RAISE NOTICE '位置记录: %', locations_count; RAISE NOTICE '轨迹记录: %', trajectories_count; RAISE NOTICE '指令记录: %', commands_count; RAISE NOTICE '违规事件记录: %', violations_count; RAISE NOTICE '=============================================='; RAISE NOTICE '主要改进:'; RAISE NOTICE '1. 统一vehicle_id为数字主键'; RAISE NOTICE '2. 统一license_plate为车牌号字段名'; RAISE NOTICE '3. 消除所有字段名不一致'; RAISE NOTICE '4. 重建了所有业务视图'; RAISE NOTICE '5. 优化了索引结构'; RAISE NOTICE '=============================================='; END $$; -- 提交事务 COMMIT; -- 显示完成消息 \echo '数据模型统一重构脚本执行完成!' \echo '请验证数据完整性和功能正常性。'