MYSQL; 存储过程,游标 ;根据时间,计算插值,将结果插入另一个表中

存储过程,游标 固定格式

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,确保查出参与运算的字段与游标在同一行

转载于:https://www.cnblogs.com/laidianjilu/p/10673612.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值