MySQL存储过程通过游标循环遍历
- 简单说明
- 存储过程详情
- 调试存储过程
简单说明
日志表迁移,从当前表迁移到历史表。
通过存储过程,实现的逻辑:查询符合迁移条件的记录,放入游标中,通过循环遍历游标,把记录迁移到历史表,同时删除当前表对应的记录。最终记录迁移日志。可针对迁移日志进行监控,已达到对迁移失败记录重新迁移等。
存储过程详情
CREATE PROCEDURE `transLogMoveProc`()
BEGIN
-- 需要定义接收游标数据的变量
declare a_id bigint(20);
declare a_user_id varchar(32) DEFAULT NULL ;
declare a_user_name varchar(80) DEFAULT NULL ;
declare a_remark1 varchar(32) DEFAULT NULL ;
declare a_version varchar(20) DEFAULT NULL ;
declare a_host_ip varchar(80) DEFAULT NULL;
declare a_code varchar(32) DEFAULT NULL ;
declare a_message varchar(100) DEFAULT NULL ;
declare a_create_time timestamp DEFAULT NULL;
declare a_create_by varchar(32) DEFAULT NULL ;
declare a_modify_time timestamp DEFAULT NULL ;
declare a_modify_by varchar(32) DEFAULT NULL ;
-- 迁移记录数
DECLARE i_count int default 0;
-- 提交记录数
DECLARE c_count int default 0;
-- 当次迁移总数
DECLARE all_count int default 0;
-- 遍历数据结束标志
DECLARE done INT DEFAULT FALSE;
-- 当次迁移成功标志
DECLARE execute_status INT DEFAULT FALSE;
DECLARE beginTime timestamp DEFAULT NULL ;
DECLARE endTime timestamp DEFAULT NULL ;
DECLARE diffTime int default 0;
DECLARE bcount int default 0;
DECLARE rs_cursor CURSOR FOR (
SELECT
id as a_id,
user_id as a_user_id,
user_name as a_user_name,
remark1 as a_remark1,
version as a_version,
code as a_code,
message as a_message,
create_time as a_create_time ,
create_by as a_create_by,
modify_time as a_modify_time ,
modify_by as a_modify_by from t_fop_trans_log_test where
TIMESTAMPDIFF(MONTH,DATE_FORMAT(tran_date,'%Y-%m-%d %H:%i:%S'),DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%S')) >= 1);
-- 将结束标志绑定到游标
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE;
set beginTime = NOW();
-- 当次迁移总数
select count(1) into all_count from t_fop_trans_log_test where
TIMESTAMPDIFF(MONTH,DATE_FORMAT(tran_date,'%Y-%m-%d %H:%i:%S'),DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%S')) >= 1;
select all_count;
-- 打开游标
OPEN rs_cursor;
set AUTOCOMMIT=0;
-- 开始循环
transLog: LOOP
-- 提取游标里的数据
FETCH rs_cursor INTO a_id, a_user_id, a_user_name, a_remark1, a_version, a_code, a_message, a_create_time, a_create_by, a_modify_time, a_modify_by;
-- 声明结束
IF done THEN
LEAVE transLog;
END IF;
-- 循环事件
-- 把符合迁移条件的记录迁移到历史表
insert into t_trans_log_his ( `id`,`user_id`, `user_name`, `remark1`, `version`, `code`, `message`, `create_time`, `create_by`, `modify_time`, `modify_by`) values (a_id, a_user_id, a_user_name, a_remark1, a_version, a_code, a_message, a_create_time, a_create_by, a_modify_time, a_modify_by);
-- 从当前表删除已经迁移的记录
DELETE FROM t_trans_log_test where id = a_ID ;
set i_count=i_count + 1;
set c_count=c_count + 1;
-- 每10000条记录提交一次,避免频繁提交io,导致执行缓慢
IF MOD(c_count,10000)=0 THEN
COMMIT;
SELECT c_count;
SET c_count=0;
END IF;
END LOOP apiLog;
COMMIT;
set endTime = NOW();
set diffTime = TIMESTAMPDIFF(SECOND,beginTime,endTime);
select beginTime;
select endTime;
select diffTime;
select i_count;
-- 总记录数与迁移记录数相等,标识迁移成功
if(i_count = all_count) then
set execute_status = TRUE;
-- 迁移记录表中记录当次记录,迁移总数,迁移开始时间,迁移结束时间。迁移月份等
insert into t_fop_apimove_log(`move_count`,`use_time`, `move_time`, `move_status`) VALUES (i_count, diffTime,DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%S'), execute_status);
select count(1) into bcount from t_fop_apimove_log;
END IF;
if (i_count != all_count) then
insert into t_fop_apimove_log(`move_count`,`use_time`, `move_time`, `move_status`) VALUES (i_count, diffTime,DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%S'), execute_status);
select cout(1) into bcount from t_fop_apimove_log;
end if;
COMMIT;
select bcount;
SELECT execute_status;
-- 关闭游标
CLOSE rs_cursor;
END
调试存储过程
写了一大串存储过程脚本后,如何调式存储过程?网上找了比较多的方法是有专门针对mysql的存储过程的客户端dbforgemysql,但是运行调试需要比较高的用户权限,一般正常使用用户是没有权限去调试的。
比较常规的方法是,存储过程中,在你想断点debug的地方,增加变量,然后select @变量 可打印出调试信息。
如:
-- 当次迁移总数
select count(1) into all_count from t_fop_trans_log_test where
TIMESTAMPDIFF(MONTH,DATE_FORMAT(tran_date,'%Y-%m-%d %H:%i:%S'),DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%S')) >= 1;
-- 这里就是调试信息,运行存储过程时,如果前面都没有报错,则可在控制台打印出all_count的值
select all_count;
同时,在正式环境中,增加迁移记录表,可监控每次迁移是否成功,这点很重要。
-- 总记录数与迁移记录数相等,标识迁移成功
if(i_count = all_count) then
set execute_status = TRUE;
-- 迁移记录表中记录当次记录,迁移总数,迁移开始时间,迁移结束时间。迁移月份等
insert into t_move_log(`move_count`,`use_time`, `move_time`, `move_status`) VALUES (i_count, diffTime,DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%S'), execute_status);
select count(1) into bcount from t_fop_apimove_log;
END IF;
if (i_count != all_count) then
insert into t_move_log(`move_count`,`use_time`, `move_time`, `move_status`) VALUES (i_count, diffTime,DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%S'), execute_status);
end if;