存储过程,游标 固定格式
DELIMITER $$DROP PROCEDURE IF EXISTS `historical_data_period`$$ CREATE PROCEDURE `historical_data_period`(IN start_time_cur VARCHAR(32),IN end_time_cur VARCHAR(32)) BEGIN -- declare声明,声明顺序(由前往后):1.变量,2.游标,3.异常 -- 1. DECLARE stopFlag INT DEFAULT 0;-- 游标默认循环 -- 2. DECLARE start_time_count CURSOR FOR -- 游标绑定一个表的所有字段 SELECT * FROM 表名 ; -- 3. -- 将结束标志绑定到游标 DECLARE CONTINUE HANDLER FOR NOT FOUND SET stopFlag = 1;-- 当查不到数据时,关闭游标 -- 游标结构体 -- 打开游标 OPEN start_time_count; read_loop: LOOP -- 取值 取多个字段 FETCH start_time_count INTO min_time,station_code2; IF stopFlag =1 THEN LEAVE read_loop; END IF; -- 你自己想做的操作 END LOOP read_loop; CLOSE start_time_count; SET stopFlag=0; -- 游标结构体 END$$ DELIMITER ;
关于表
/** 本例未考虑主键,如果考虑主键,值非空,对数据操作(插入,更新)优先操作;未考虑计算的单位 创建表注意:末尾的 逗号,无符号 DEFAULT NULL:也可以 DECLARE day_count INT(8) DEFAULT TO_DAYS(end_time_cur) - TO_DAYS(start_time_cur); */ CREATE TABLE `historical_data` ( -- 要查询的表 `heat_station_code` CHAR(32) DEFAULT NULL,-- 热站编码 `heat_value` DECIMAL(32,2) DEFAULT NULL,-- 最小表值 read_time DATETIME DEFAULT NULL, -- 数据读取时间 heat_area_design DECIMAL(32,2) DEFAULT NULL -- 计算耗热量 ) CREATE TABLE `period_data` ( -- 要插入的表 `heat_station_code` CHAR(32) DEFAULT NULL,-- 热站编码 `heat_meter_start_value` DECIMAL(32,2) UNSIGNED DEFAULT NULL, `heat_meter_end_value` DECIMAL(32,2) UNSIGNED DEFAULT NULL, `heat_consumption` DECIMAL(32,2) DEFAULT NULL,-- 表值差,耗热量 heat_area_design DECIMAL(32,2) DEFAULT NULL, -- 计算耗热量 `heat_rate` DECIMAL(32,2) DEFAULT NULL -- 耗热率 耗热量/ 面积 ) -- ALTER TABLE `period_data` ADD day_count INT(32,2); 可加表字段 -- CALL historical_data_period('2019-03-25 20:55:00','2019-03-26 22:55:00',50);-- 插入表数据,注意'时间'datetime格式
historical_data
需求1:取【historical_data表】的【两个read_time值】,【两个read_time值】对应【两个heat_value值】。计算最早一天和最后一天的耗热量(heat_consumption)和耗热率(heat_rate)
假使3个热站,会查出3条记录
DELIMITER $$DROP PROCEDURE IF EXISTS `historical_data_period`$$ CREATE PROCEDURE `historical_data_period`(IN start_time_cur VARCHAR(32),IN end_time_cur VARCHAR(32)) BEGIN -- 定义变量 -- 事务 游标 变量 -- DECLARE station_code2 VARCHAR(32) DEFAULT NULL; -- 热站编码 DECLARE heat_area_design2 VARCHAR(32) DEFAULT NULL; -- 热站编码 DECLARE min_time DATETIME DEFAULT NULL;-- 起始时间`read_time`当天最早的一条记录 DECLARE max_time DATETIME DEFAULT NULL;-- 结束时间`read_time`当天最晚的一条记录 DECLARE min_heat_value DOUBLE(255,2) DEFAULT NULL;-- min_time对应的表值 DECLARE max_heat_value DOUBLE(255,2) DEFAULT NULL;-- max_time对应的表值 DECLARE heat_consumption_count DOUBLE(255,2) DEFAULT NULL; -- 耗热量 DECLARE day_count INT(8) DEFAULT TO_DAYS(end_time_cur) - TO_DAYS(start_time_cur);-- 定义时间间隔 DECLARE stopFlag INT DEFAULT 0;-- 设置游标默认循环 -- 定义游标 DECLARE start_time_count CURSOR FOR -- 第一天记录的最早一条数据 SELECT MIN(`read_time`),`heat_station_code` FROM `historical_data` WHERE TO_DAYS(start_time_cur) - TO_DAYS(read_time)=0 GROUP BY `heat_station_code`; -- 按热站编码生成3行数据 DECLARE end_time_count CURSOR FOR -- 最后一天记录的最晚一条数据 SELECT MAX(`read_time`) FROM `historical_data` WHERE TO_DAYS(end_time_cur) - TO_DAYS(read_time)=0 AND heat_station_code=station_code2; -- 将结束标志绑定到游标 DECLARE CONTINUE HANDLER FOR NOT FOUND SET stopFlag = 1;-- 当查不到数据时,关闭游标 -- 手动开启事务 START TRANSACTION; -- 清除表中上次存储的查询数据 TRUNCATE TABLE `period_data`; -- 打开游标 OPEN start_time_count; read_loop: LOOP -- 取值 取多个字段 FETCH start_time_count INTO min_time,station_code2; IF stopFlag =1 THEN LEAVE read_loop; END IF; -- 你自己想做的操作 SELECT `heat_value` INTO min_heat_value FROM `historical_data` WHERE read_time=min_time AND heat_station_code=station_code2 ; -- 第二个游标 OPEN end_time_count; next_loop: LOOP -- 取值 取多个字段 FETCH end_time_count INTO max_time; IF stopFlag=1 THEN LEAVE next_loop; END IF; -- 你自己想做的操作 SELECT `heat_value` INTO max_heat_value FROM `historical_data` WHERE read_time=max_time AND heat_station_code=station_code2 ; SET heat_area_design2=(SELECT `heat_area_design` FROM `historical_data` WHERE `heat_station_code`=station_code2 AND `read_time`=min_time); SET heat_consumption_count= max_heat_value - min_heat_value; -- 若计算出来的能耗量为null或为负值,赋值为0 IF(heat_consumption_count IS NULL||heat_consumption_count <0 ) THEN SET heat_consumption_count=0; END IF; -- heat_consumption_count IS NULL -- 插入热站信息 /** 1.如果计算耗热率时为分母为null,设置耗热率为0 2.TRUNCATE 小数点后两位的数,直接截断 3.虽然格式是 SELECT..from..,但INSERT INTO的字段可以不是from表中的字段 4.WHERE中 `read_time`=min_time也可以改为`read_time`=max_time 因为从historical_data中的SELECT的字段,不受AND `read_time`=min_time;时间限制 但AND `read_time`=min_time也不可以省略。因为`heat_station_code`=station_code2的记录每天都有,`heat_station_code`=station_code2的记录有很多行,
AND `read_time`=min_time保证不取重复的热站编码 */ IF(heat_area_design2 IS NOT NULL) THEN INSERT INTO `period_data`(`heat_station_code`,`heat_meter_start_value`,`heat_meter_end_value`,heat_consumption,heat_area_design,heat_rate) SELECT `heat_station_code`,min_heat_value,max_heat_value,heat_consumption_count,heat_area_design2,TRUNCATE(heat_consumption_count/(heat_area_design2),2) FROM `historical_data` WHERE`heat_station_code`=station_code2 AND `read_time`=min_time; ELSE INSERT INTO `period_data`(`heat_station_code`,`heat_meter_start_value`,`heat_meter_end_value`,heat_consumption,`heat_area_design`,heat_rate) SELECT `heat_station_code`,min_heat_value,max_heat_value,heat_consumption_count,heat_area_design2,0 FROM `historical_data` WHERE `heat_station_code`=station_code2 AND `read_time`=min_time; END IF;-- day_time_difference!=0 END LOOP next_loop; CLOSE end_time_count; /* 第二个游标 // 内循环游标指到最后一条数据,再继续,便会not found,关闭游标, 内外循环共用一个stopFlag, 要进入外循环,必须打开游标, */ SET stopFlag=0; END LOOP read_loop; CLOSE start_time_count; COMMIT; END$$ DELIMITER ;
CALL historical_data_period('2019-03-25 20:55:00','2019-03-26 22:55:00');
period_data
需求2:时间改变,【两个read_time值】,即两个时间段,求两个时间段内 每天的耗热量(heat_consumption)和耗热率
假使3个热站,会查出(3*(间隔时间+1))条记录:4月1日与4月3日的间隔时间时3-1=2天,但包括4月1日与4月3日这两天,总天数该是3天
DELIMITER $$DROP PROCEDURE IF EXISTS `historical_data_period`$$ CREATE PROCEDURE `historical_data_period`(IN start_time_cur VARCHAR(32),IN end_time_cur VARCHAR(32)) BEGIN -- 定义变量 -- 事务 游标 变量 DECLARE station_code2 VARCHAR(32) DEFAULT NULL; -- 热站编码 DECLARE heat_area_design2 VARCHAR(32) DEFAULT NULL; -- 热站编码 DECLARE min_time DATETIME DEFAULT NULL;-- 起始时间`read_time`当天最早的一条记录 DECLARE max_time DATETIME DEFAULT NULL;-- 结束时间`read_time`当天最晚的一条记录 DECLARE min_heat_value DOUBLE(255,2) DEFAULT NULL;-- min_time对应的表值 DECLARE max_heat_value DOUBLE(255,2) DEFAULT NULL;-- max_time对应的表值 DECLARE heat_consumption_count DOUBLE(255,2) DEFAULT NULL; -- 耗热量 DECLARE day_count INT(8) DEFAULT TO_DAYS(end_time_cur) - TO_DAYS(start_time_cur);-- 定义时间间隔 DECLARE stopFlag INT DEFAULT 0;-- 设置游标默认循环 -- 将结束标志绑定到游标 DECLARE CONTINUE HANDLER FOR NOT FOUND SET stopFlag = 1;-- 当查不到数据时,关闭游标 -- 手动开启事务 START TRANSACTION; -- 清除表中上次存储的查询数据 TRUNCATE TABLE `period_data`;
-- <改变的地方 /*定位到时间段内的当天时间 ,从前向后搜 -- 设置按时间*/ WHILE( day_count>= 0) DO IF(EXISTS(SELECT * FROM `historical_data` WHERE TO_DAYS(end_time_cur) - TO_DAYS(read_time)=day_count)) THEN -- 第一天 BEGIN -- 保证游标定义能在后面! -- 定义游标 DECLARE start_time_count CURSOR FOR -- 当天记录的最早一条数据 SELECT MIN(`read_time`),`heat_station_code` FROM `historical_data` WHERE TO_DAYS(end_time_cur) - TO_DAYS(read_time)=day_count GROUP BY `heat_station_code`; -- 按热站编码生成3行数据 DECLARE end_time_count CURSOR FOR -- 当记录的最晚一条数据 SELECT MAX(`read_time`) FROM `historical_data` WHERE TO_DAYS(end_time_cur) - TO_DAYS(read_time)=day_count AND heat_station_code=station_code2; -- 改变的地方> -- 打开游标 OPEN start_time_count; read_loop: LOOP -- 取值 取多个字段 FETCH start_time_count INTO min_time,station_code2; IF stopFlag =1 THEN LEAVE read_loop; END IF; -- 你自己想做的操作 SELECT `heat_value` INTO min_heat_value FROM `historical_data` WHERE read_time=min_time AND heat_station_code=station_code2 ; -- 第二个游标 OPEN end_time_count; next_loop: LOOP -- 取值 取多个字段 FETCH end_time_count INTO max_time; IF stopFlag=1 THEN LEAVE next_loop; END IF; -- 你自己想做的操作 SELECT `heat_value` INTO max_heat_value FROM `historical_data` WHERE read_time=max_time AND heat_station_code=station_code2 ; SET heat_area_design2=(SELECT `heat_area_design` FROM `historical_data` WHERE `heat_station_code`=station_code2 AND `read_time`=min_time); SET heat_consumption_count= max_heat_value - min_heat_value; -- 若计算出来的能耗量为null或为负值,赋值为0 IF(heat_consumption_count IS NULL||heat_consumption_count <0 ) THEN SET heat_consumption_count=0; END IF; -- heat_consumption_count IS NULL -- 插入热站信息 /** 如果计算耗热率时为分母为0,设置耗热率为0 TRUNCATE 小数点后两位的数,直接截断 虽然格式是 SELECT..from..,但INSERT INTO的字段可以不是from表中的字段 WHERE中 `read_time`=min_time也可以改为`read_time`=max_time 因为从historical_data中的SELECT的字段,只有heat_station_code,heat_station_code只受WHERE `heat_station_code`=station_code2限制,不受AND `read_time`=min_time;时间限制 AND `read_time`=min_time不可以省略。因为`heat_station_code`=station_code2的记录每天都有,`heat_station_code`=station_code2的记录有很多行,AND `read_time`=min_time保证不取重复的热站编码 */ IF(heat_area_design2 IS NOT NULL) THEN INSERT INTO `period_data`(`heat_station_code`,`heat_meter_start_value`,`heat_meter_end_value`,heat_consumption,heat_area_design,heat_rate) SELECT `heat_station_code`,min_heat_value,max_heat_value,heat_consumption_count,heat_area_design2,TRUNCATE(heat_consumption_count/(heat_area_design2),2) FROM `historical_data` WHERE`heat_station_code`=station_code2 AND `read_time`=min_time; ELSE INSERT INTO `period_data`(`heat_station_code`,`heat_meter_start_value`,`heat_meter_end_value`,heat_consumption,`heat_area_design`,heat_rate) SELECT `heat_station_code`,min_heat_value,max_heat_value,heat_consumption_count,heat_area_design2,0 FROM `historical_data` WHERE `heat_station_code`=station_code2 AND `read_time`=min_time; END IF;-- day_time_difference!=0 END LOOP next_loop; CLOSE end_time_count; /* 第二个游标 // 内循环游标指到最后一条数据,再继续,便会not found,关闭游标, 内外循环共用一个stopFlag, 要进入外循环,必须打开游标, */ SET stopFlag=0; END LOOP read_loop; CLOSE start_time_count;
-- <改变的地方 SET stopFlag=0; END; -- end后是要加结束符号的 END IF; SET day_count=day_count-1; END WHILE;
-- 改变的地方> COMMIT; END$$ DELIMITER ;
CALL historical_data_period('2019-03-25 20:55:00','2019-03-27 00:55:44');
period_data
关于游标:游标定义时,未带能计算的字段,要取出参与运算的字段,用select语句设置where,确保查出参与运算的字段与游标在同一行