智慧水务管理系统 - 精河县供水工程综合管理平台

V1.1__Add_Indexes_And_Triggers.sql 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. -- Flyway 迁移脚本 V1.1
  2. -- 添加优化索引和触发器
  3. -- 执行时间: 2026-06-15
  4. -- ========= 性能优化索引 =========
  5. -- 物联网设备相关查询优化索引
  6. CREATE INDEX IF NOT EXISTS idx_iot_device_type_area_status ON iot_device(device_type, area, status);
  7. CREATE INDEX IF NOT EXISTS idx_iot_device_geom_area ON iot_device USING GIST(geom) WHERE area IS NOT NULL;
  8. CREATE INDEX IF NOT EXISTS idx_iot_device_last_report_time ON iot_device(last_report_time DESC);
  9. -- 设备遥测数据时间范围查询优化
  10. CREATE INDEX IF NOT EXISTS idx_iot_telemetry_device_time ON iot_telemetry_cache(device_sn, ts DESC);
  11. CREATE INDEX IF NOT EXISTS idx_iot_telemetry_metric_time ON iot_telemetry_cache(metric_key, ts DESC);
  12. CREATE INDEX IF NOT EXISTS idx_iot_telemetry_device_metric_time ON iot_telemetry_cache(device_sn, metric_key, ts DESC);
  13. -- 营业收费业务查询优化索引
  14. CREATE INDEX IF NOT EXISTS idx_rev_customer_area_status ON rev_customer(area, status);
  15. CREATE INDEX IF NOT EXISTS idx_rev_meter_customer_status ON rev_meter(customer_id, status);
  16. CREATE INDEX IF NOT EXISTS idx_rev_meter_device ON rev_meter(device_id);
  17. CREATE INDEX IF NOT EXISTS idx_rev_reading_meter_date ON rev_reading(meter_id, reading_date);
  18. CREATE INDEX IF NOT EXISTS idx_rev_bill_customer_period ON rev_bill(customer_id, bill_period);
  19. CREATE INDEX IF NOT EXISTS idx_rev_bill_status_date ON rev_bill(status, due_date);
  20. -- 巡检系统查询优化索引
  21. CREATE INDEX IF NOT EXISTS idx_patrol_route_area_status ON patrol_route(area, status);
  22. CREATE INDEX IF NOT EXISTS idx_patrol_task_assignee_date ON patrol_task(assignee_id, task_date);
  23. CREATE INDEX IF NOT EXISTS idx_patrol_task_route_date ON patrol_task(route_id, task_date);
  24. CREATE INDEX IF NOT EXISTS idx_patrol_record_task_point ON patrol_record(task_id, point_seq);
  25. CREATE INDEX IF NOT EXISTS idx_patrol_record_device_time ON patrol_record(device_id, record_time);
  26. -- 报警管理查询优化索引
  27. CREATE INDEX IF NOT EXISTS idx_alert_rule_enabled_level ON alert_rule(enabled, alert_level);
  28. CREATE INDEX IF NOT EXISTS idx_alert_event_device_time ON alert_event(device_sn, created_at DESC);
  29. CREATE INDEX IF NOT EXISTS idx_alert_event_level_status ON alert_event(alert_level, dispatched);
  30. CREATE INDEX IF NOT EXISTS idx_alert_event_rule_time ON alert_event(rule_id, created_at DESC);
  31. -- 系统日志查询优化索引
  32. CREATE INDEX IF NOT EXISTS idx_sys_oplog_user_time ON sys_operation_log(user_id, created_at DESC);
  33. CREATE INDEX IF NOT EXISTS idx_sys_oplog_type_time ON sys_operation_log(operation_type, created_at DESC);
  34. CREATE INDEX IF NOT EXISTS idx_sys_oplog_target ON sys_operation_log(target_type, target_id);
  35. -- 用户权限相关优化索引
  36. CREATE INDEX IF NOT EXISTS idx_sys_user_role_active ON sys_user(role_type) WHERE status = 1;
  37. CREATE INDEX IF NOT EXISTS idx_sys_role_menu_active ON sys_menu(status) WHERE status = 1;
  38. CREATE INDEX IF NOT EXISTS idx_sys_config_key_active ON sys_config(config_key) WHERE status = 1;
  39. -- ========= 复合查询优化索引 =========
  40. -- 复合业务查询索引
  41. CREATE INDEX IF NOT EXISTS idx_iot_device_customer_flow ON iot_device
  42. WHERE device_type = 'flow_meter' AND area = '精芒片区';
  43. -- 账单状态和到期时间复合索引
  44. CREATE INDEX IF NOT EXISTS idx_rev_bill_overdue ON rev_bill(status, due_date)
  45. WHERE status IN ('pending', 'overdue');
  46. -- 报警紧急程度和状态复合索引
  47. CREATE INDEX IF NOT EXISTS idx_alert_urgent ON alert_event(alert_level, dispatched)
  48. WHERE alert_level IN ('critical', 'emergency');
  49. -- 巡检任务优先级索引
  50. CREATE INDEX IF NOT EXISTS idx_patrol_priority ON patrol_task
  51. WHERE status = 'pending' AND task_date = CURRENT_DATE;
  52. -- ========= 分区索引(如果需要) =========
  53. -- 对于大数据量表,可以考虑按时间分区
  54. -- 例如:按月分区大表的数据
  55. -- ========= 触发器优化 =========
  56. -- 自动更新时间触发器优化版
  57. CREATE OR REPLACE FUNCTION update_updated_timestamp()
  58. RETURNS TRIGGER AS $$
  59. BEGIN
  60. NEW.updated_at = CURRENT_TIMESTAMP;
  61. RETURN NEW;
  62. END;
  63. $$ LANGUAGE plpgsql;
  64. -- 为主要表添加自动更新触发器
  65. DROP TRIGGER IF EXISTS trg_iot_device_update ON iot_device;
  66. CREATE TRIGGER trg_iot_device_update
  67. BEFORE UPDATE ON iot_device
  68. FOR EACH ROW EXECUTE FUNCTION update_updated_timestamp();
  69. DROP TRIGGER IF EXISTS trg_rev_customer_update ON rev_customer;
  70. CREATE TRIGGER trg_rev_customer_update
  71. BEFORE UPDATE ON rev_customer
  72. FOR EACH ROW EXECUTE FUNCTION update_updated_timestamp();
  73. DROP TRIGGER IF EXISTS trg_rev_meter_update ON rev_meter;
  74. CREATE TRIGGER trg_rev_meter_update
  75. BEFORE UPDATE ON rev_meter
  76. FOR EACH ROW EXECUTE FUNCTION update_updated_timestamp();
  77. DROP TRIGGER IF EXISTS trg_rev_bill_update ON rev_bill;
  78. CREATE TRIGGER trg_rev_bill_update
  79. BEFORE UPDATE ON rev_bill
  80. FOR EACH ROW EXECUTE FUNCTION update_updated_timestamp();
  81. DROP TRIGGER IF EXISTS trg_alert_event_update ON alert_event;
  82. CREATE TRIGGER trg_alert_event_update
  83. BEFORE UPDATE ON alert_event
  84. FOR EACH ROW EXECUTE FUNCTION update_updated_timestamp();
  85. -- ========= 数据完整性约束 =========
  86. -- 添加外键约束检查
  87. ALTER TABLE iot_device
  88. ADD CONSTRAINT fk_iot_device_model
  89. FOREIGN KEY (model_id) REFERENCES iot_device_model(id) ON DELETE RESTRICT;
  90. ALTER TABLE rev_meter
  91. ADD CONSTRAINT fk_rev_meter_customer
  92. FOREIGN KEY (customer_id) REFERENCES rev_customer(id) ON DELETE CASCADE;
  93. ALTER TABLE rev_meter
  94. ADD CONSTRAINT fk_rev_meter_device
  95. FOREIGN KEY (device_id) REFERENCES iot_device(id) ON DELETE SET NULL;
  96. ALTER TABLE rev_reading
  97. ADD CONSTRAINT fk_rev_reading_meter
  98. FOREIGN KEY (meter_id) REFERENCES rev_meter(id) ON DELETE CASCADE;
  99. ALTER TABLE rev_bill
  100. ADD CONSTRAINT fk_rev_bill_customer
  101. FOREIGN KEY (customer_id) REFERENCES rev_customer(id) ON DELETE CASCADE;
  102. ALTER TABLE alert_event
  103. ADD CONSTRAINT fk_alert_event_rule
  104. FOREIGN KEY (rule_id) REFERENCES alert_rule(id) ON DELETE CASCADE;
  105. ALTER TABLE alert_event
  106. ADD CONSTRAINT fk_alert_event_device
  107. FOREIGN KEY (device_id) REFERENCES iot_device(id) ON DELETE SET NULL;
  108. -- ========= 检查约束 =========
  109. -- 添加业务规则检查约束
  110. ALTER TABLE iot_device
  111. ADD CONSTRAINT chk_device_status
  112. CHECK (status IN ('online', 'offline', 'maintenance', 'fault'));
  113. ALTER TABLE rev_customer
  114. ADD CONSTRAINT chk_customer_status
  115. CHECK (status IN ('active', 'inactive', 'suspended'));
  116. ALTER TABLE rev_bill
  117. ADD CONSTRAINT chk_bill_status
  118. CHECK (status IN ('pending', 'partial', 'paid', 'overdue'));
  119. ALTER TABLE alert_event
  120. ADD CONSTRAINT chk_alert_level
  121. CHECK (alert_level IN ('info', 'warning', 'critical', 'emergency'));
  122. -- ========= 视图创建(优化常用查询) =========
  123. -- 设备状态统计视图
  124. CREATE OR REPLACE VIEW v_device_status_stats AS
  125. SELECT
  126. area,
  127. device_type,
  128. status,
  129. COUNT(*) as device_count,
  130. COUNT(CASE WHEN last_report_time > CURRENT_TIMESTAMP - INTERVAL '1 hour' THEN 1 END) as online_count,
  131. COUNT(CASE WHEN last_report_time <= CURRENT_TIMESTAMP - INTERVAL '1 hour' THEN 1 END) as offline_count
  132. FROM iot_device
  133. GROUP BY area, device_type, status;
  134. -- 区域用水量统计视图
  135. CREATE OR REPLACE VIEW v_water_consumption_stats AS
  136. SELECT
  137. c.area,
  138. c.customer_type,
  139. COUNT(c.id) as customer_count,
  140. COUNT(m.id) as meter_count,
  141. COALESCE(SUM(r.consumption), 0) as total_consumption,
  142. COALESCE(AVG(r.consumption), 0) as avg_consumption
  143. FROM rev_customer c
  144. LEFT JOIN rev_meter m ON c.id = m.customer_id AND m.status = 'active'
  145. LEFT JOIN rev_reading r ON m.id = r.meter_id AND r.reading_date >= CURRENT_DATE - INTERVAL '30 days'
  146. GROUP BY c.area, c.customer_type;
  147. -- 报警事件统计视图
  148. CREATE OR REPLACE VIEW v_alert_stats AS
  149. SELECT
  150. area,
  151. alert_level,
  152. COUNT(*) as alert_count,
  153. COUNT(CASE WHEN resolved_at IS NULL THEN 1 END) as pending_count,
  154. COUNT(CASE WHEN resolved_at IS NOT NULL THEN 1 END) as resolved_count,
  155. COUNT(CASE WHEN created_at > CURRENT_TIMESTAMP - INTERVAL '24 hours' THEN 1 END) as recent_count
  156. FROM alert_event a
  157. JOIN iot_device d ON a.device_sn = d.device_sn
  158. GROUP BY area, alert_level;
  159. -- 巡检任务执行统计视图
  160. CREATE OR REPLACE VIEW v_patrol_stats AS
  161. SELECT
  162. r.area,
  163. COUNT(t.id) as task_count,
  164. COUNT(CASE WHEN t.status = 'completed' THEN 1 END) as completed_count,
  165. COUNT(CASE WHEN t.status = 'pending' THEN 1 END) as pending_count,
  166. COUNT(CASE WHEN t.status = 'in_progress' THEN 1 END) as in_progress_count
  167. FROM patrol_route r
  168. LEFT JOIN patrol_task t ON r.id = t.route_id AND t.task_date >= CURRENT_DATE - INTERVAL '30 days'
  169. GROUP BY r.area;
  170. -- ========= 函数创建(业务逻辑封装) =========
  171. -- 获取设备最新数据的函数
  172. CREATE OR REPLACE FUNCTION get_device_latest_metrics(device_sn VARCHAR(100))
  173. RETURNS TABLE(metric_key VARCHAR(50), metric_value DECIMAL(20,4), ts TIMESTAMP) AS $$
  174. BEGIN
  175. RETURN QUERY
  176. SELECT DISTINCT ON (metric_key)
  177. metric_key,
  178. metric_value,
  179. ts
  180. FROM iot_telemetry_cache
  181. WHERE device_sn = device_sn
  182. ORDER BY metric_key, ts DESC;
  183. END;
  184. $$ LANGUAGE plpgsql;
  185. -- 计算水费的函数
  186. CREATE OR REPLACE FUNCTION calculate_water_fee(customer_id BIGINT, period VARCHAR(10))
  187. RETURNS TABLE(consumption DECIMAL(10,2), water_fee DECIMAL(10,2), sewage_fee DECIMAL(10,2), total_fee DECIMAL(10,2)) AS $$
  188. DECLARE
  189. water_rate RECORD;
  190. usage DECIMAL(10,2);
  191. total_consumption DECIMAL(10,2) := 0;
  192. fee DECIMAL(10,2) := 0;
  193. sewage_charge DECIMAL(10,2) := 0;
  194. BEGIN
  195. -- 获取用水量
  196. SELECT COALESCE(SUM(consumption), 0) INTO total_consumption
  197. FROM rev_reading r
  198. JOIN rev_meter m ON r.meter_id = m.id
  199. WHERE m.customer_id = customer_id
  200. AND r.reading_date LIKE period || '%';
  201. -- 获取水费标准
  202. SELECT * INTO water_rate
  203. FROM rev_water_rate
  204. WHERE customer_type = (SELECT customer_type FROM rev_customer WHERE id = customer_id)
  205. AND start_date <= to_date(period, 'YYYY-MM')
  206. AND (end_date IS NULL OR end_date >= to_date(period, 'YYYY-MM'))
  207. ORDER BY start_date DESC
  208. LIMIT 1;
  209. -- 计算阶梯水费
  210. IF water_rate.step_threshold IS NOT NULL AND water_rate.step_rates IS NOT NULL THEN
  211. -- 实现阶梯水费计算逻辑
  212. fee := total_consumption * 2.5; -- 简化计算
  213. ELSE
  214. fee := total_consumption * (SELECT (jsonb_array_elements_text(water_rate.step_rates))[1]::decimal FROM jsonb_array_elements(water_rate.step_rates) LIMIT 1);
  215. END IF;
  216. -- 污水费(按水量的80%计算)
  217. sewage_charge := total_consumption * 0.8 * 1.2;
  218. RETURN QUERY VALUES (total_consumption, fee, sewage_charge, fee + sewage_charge);
  219. END;
  220. $$ LANGUAGE plpgsql;
  221. -- 获取报警历史函数
  222. CREATE OR REPLACE FUNCTION get_alert_history(area VARCHAR(50), days INT DEFAULT 30)
  223. RETURNS TABLE(alert_id BIGINT, device_sn VARCHAR(100), alert_level VARCHAR(10), message TEXT, created_at TIMESTAMP) AS $$
  224. BEGIN
  225. RETURN QUERY
  226. SELECT a.id, a.device_sn, a.alert_level, a.message, a.created_at
  227. FROM alert_event a
  228. JOIN iot_device d ON a.device_sn = d.device_sn
  229. WHERE d.area = area
  230. AND a.created_at >= CURRENT_TIMESTAMP - INTERVAL '1 day' * days
  231. ORDER BY a.created_at DESC;
  232. END;
  233. $$ LANGUAGE plpgsql;
  234. -- ========= 存储过程创建(批量操作) =========
  235. -- 批量插入设备数据的存储过程
  236. CREATE OR REPLACE PROCEDURE batch_insert_device_metrics(
  237. IN device_sn_list VARCHAR(100)[],
  238. IN metric_data JSONB[]
  239. ) AS $$
  240. BEGIN
  241. FOREACH device_sn IN ARRAY device_sn_list LOOP
  242. FOREACH metric IN ARRAY metric_data LOOP
  243. INSERT INTO iot_telemetry_cache (device_sn, metric_key, metric_value, ts, quality)
  244. VALUES (
  245. device_sn,
  246. metric->>'metric_key',
  247. (metric->>'metric_value')::DECIMAL(20,4),
  248. CURRENT_TIMESTAMP,
  249. (metric->>'quality')::SMALLINT
  250. );
  251. END LOOP;
  252. END LOOP;
  253. END;
  254. $$ LANGUAGE plpgsql;
  255. -- ========= 更新 Flyway 迁移记录 =========
  256. -- 此迁移脚本 V1.1 执行完成
  257. -- 添加了性能优化索引、触发器、约束、视图和函数
  258. -- 用于提升系统查询性能和数据完整性