mysql 函数 存储过程 事件(event) job 模板
mysql的编程能力比sqlserver和mysql弱很多,万不得已尽量将这些东西转移到应用层。
but,有时候小项目或项目刚开始尝试阶段,需要降低服务器成本,压缩项目规模。期初业务需求不清晰,也比较简单,为了快速成型也可以先凑合着用。
创建函数模板:
-- ---------------------------- -- Function structure for f_get_childs -- ---------------------------- DROP FUNCTION IF EXISTS `f_get_childs`; DELIMITER ;; CREATE FUNCTION `f_get_childs`(root_id INT) RETURNS varchar(1000) CHARSET utf8 -- 有些工具如Navicat,只需要BEGIN到END这一段,SQLyog需要从DELIMITER到最后 BEGIN DECLARE ptemp VARCHAR(1000); DECLARE ctemp VARCHAR(1000); SET ptemp = '#'; SET ctemp =CAST(root_id AS CHAR); WHILE ctemp IS NOT NULL DO SET ptemp = CONCAT(ptemp,',',ctemp); SELECT GROUP_CONCAT(canton_id) INTO ctemp FROM sys_canton_info WHERE FIND_IN_SET(parent_id,ctemp)>0; END WHILE; RETURN ptemp; END ;; DELIMITER ;
创建存储过程模板:
-- ---------------------------- -- Procedure structure for p_write_log -- ---------------------------- DROP PROCEDURE IF EXISTS `p_write_log`; DELIMITER ;; CREATE PROCEDURE `p_write_log`(IN p_obj_name varchar(64), IN p_err_desc varchar(255), IN p_sql_err varchar(1000)) BEGIN DECLARE v_error BOOL DEFAULT FALSE; DECLARE CONTINUE HANDLER FOR SQLWARNING,NOT FOUND, SQLEXCEPTION SET v_error = TRUE; START TRANSACTION; INSERT INTO sys_log(object_name, error_code, error_desc, trace) VALUES (p_obj_name, 'DB-FATAL', LEFT(p_err_desc, 255), LEFT(p_sql_err, 255)); IF v_error = TRUE THEN ROLLBACK; ELSE COMMIT; END IF; END ;; DELIMITER ;
-- ---------------------------- -- Procedure structure for p_coupon_cdk_generate -- ---------------------------- DROP PROCEDURE IF EXISTS `p_coupon_cdk_generate`; DELIMITER ;; CREATE PROCEDURE `p_coupon_cdk_generate`(IN r_num bigint) BEGIN DECLARE r_code varchar(32) charset utf8 default ''; declare r_msg varchar(255) charset utf8 default 'success'; declare l_count bigint default 0; declare l_code varchar(32) charset utf8 default ''; declare l_counter bigint default 0; declare exit handler for sqlexception begin -- 先把错误取出来 get diagnostics condition 1 r_code = returned_sqlstate, r_msg = message_text; rollback; call p_write_log('p_coupon_cdk_generate', concat('存储过程执行异常'), concat('errcode:', ifnull(r_code, 'no-err-code'), ',errmsg:', ifnull(r_msg, 'no-err-msg'))); select concat(ifnull(r_code, 'no-err-code'), '|', ifnull(r_msg, 'no-err-msg')); commit; end; start transaction; label_pro: begin set l_counter = r_num; while l_counter > 0 do set l_code = upper(right(MD5(uuid()), 6)); if length(l_code + 0) = length(l_code) > 0 then -- 通过随机的uu_id的MD5来取(纯数字的不要[遇到纯数字,跳出循环,等待下次执行]) set r_msg = '纯数字,退出'; leave label_pro; end if; select count(0) into l_count from xy_coupon_cdk t where t.cdk = l_code; if l_count > 0 then set r_msg = '重复的邀请码,退出'; leave label_pro; end if; insert into xy_coupon_cdk(cdk) values (l_code); set l_counter = l_counter - 1; end while; end; commit; select concat('流程结束!', r_msg, ',条数:', r_num - l_counter); END ;; DELIMITER ;
CREATE PROCEDURE `auto_fans_notify_handler`() BEGIN DECLARE v_result VARCHAR(100) default ""; DECLARE v_total_count INT DEFAULT 100; declare v_notify_limit int default 3; DECLARE v_queue_name VARCHAR(64) default "wx.fans.sync.notify"; declare l_notify_id varchar(32); declare done int default false; DECLARE err_code CHAR(5) DEFAULT '00000'; DECLARE err_msg TEXT; DECLARE items CURSOR FOR (SELECT t.notify_id FROM wx_fans_notify t WHERE t.notify_status in (20,30) and t.notify_times <= v_notify_limit AND t.next_notify_time < NOW() AND t.next_notify_time > subdate(NOW(),INTERVAL 1 DAY) LIMIT v_total_count); DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN -- 先回滚,再保存错误日志 GET DIAGNOSTICS CONDITION 1 err_code = RETURNED_SQLSTATE, err_msg = MESSAGE_TEXT; ROLLBACK; -- RollBack之后 error信息就取不到了(FUCK)(所以得先把错误取出来再回滚!!) CALL wx_p_write_log('auto_fans_notify_handler','执行异常',CONCAT(IFNULL(err_code,'null1'), IFNULL(err_msg,'null2'))); SELECT CONCAT(IFNULL(err_code,'null1'), IFNULL(err_msg,'null2')); COMMIT; END; open items; xloop: Loop fetch items into l_notify_id; if done then leave xloop; end if; update wx_fans_notify t set t.notify_status = 30, t.notify_times = t.notify_times + 1, t.next_notify_time = adddate(NOW(),interval 5 minute) where t.notify_id = l_notify_id and t.notify_status in (20,30); if row_count() > 0 then set v_result = wx_f_message_task_create(v_queue_name,concat('{"notify_id":"',l_notify_id,'"}')); if (v_result = "success") then commit; else rollback; end if; end if; end loop xloop; close items; END
创建事件模板:
-- ---------------------------- -- Event structure for auto_p_sys_data_clear -- ---------------------------- DROP EVENT IF EXISTS `auto_p_sys_data_clear`; DELIMITER ;; CREATE EVENT `auto_p_sys_data_clear` ON SCHEDULE EVERY 1 HOUR STARTS '2019-09-27 15:29:39' ON COMPLETION PRESERVE ENABLE DO BEGIN DECLARE r_code CHAR(5) DEFAULT '00000'; DECLARE r_msg TEXT CHARACTER SET utf8 DEFAULT '执行成功'; DECLARE v_obj_name VARCHAR(100) DEFAULT 'p_sys_data_clear'; DECLARE v_error BOOL DEFAULT FALSE; DECLARE v_start_time DATETIME DEFAULT NOW(); DECLARE v_serial_no VARCHAR(100) DEFAULT CONCAT(v_obj_name, ':', REPLACE(UUID(), '-', '')); INSERT INTO `sys_job_log`(`object_name`, start_time, `serial_no`) VALUES (v_obj_name, v_start_time, v_serial_no); BEGIN #异常处理段 DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN SET v_error = TRUE; GET DIAGNOSTICS CONDITION 1 r_code = RETURNED_SQLSTATE , r_msg = MESSAGE_TEXT; END; CALL p_sys_data_clear(); -- 这里将核心逻辑和event分离,event只管定时调度,核心逻辑尽量用存储过程实现 END; -- 相当于记录调度日志 UPDATE `sys_job_log` t SET t.`status` = IF(v_error, 90, 0), t.`end_time` = NOW(), t.`duration_time` = TIMESTAMPDIFF(SECOND, v_start_time, NOW()), t.`result_msg` = CONCAT(r_code, '|', r_msg) WHERE t.`serial_no` = v_serial_no AND t.`object_name` = v_obj_name AND t.`status` = 30; END ;; DELIMITER ;
-- ---------------------------- -- Event structure for auto_p_order_sync -- ---------------------------- DROP EVENT IF EXISTS `auto_p_order_sync`; DELIMITER ;; CREATE EVENT `auto_p_order_sync` ON SCHEDULE EVERY 3 SECOND STARTS '2019-09-27 15:42:25' ON COMPLETION PRESERVE ENABLE DO BEGIN DECLARE r_code CHAR(5) DEFAULT '00000'; DECLARE r_msg TEXT CHARACTER SET utf8 DEFAULT '执行成功'; DECLARE v_obj_name VARCHAR(100) DEFAULT 'p_order_pool_sync'; DECLARE v_error BOOL DEFAULT FALSE; DECLARE v_start_time DATETIME DEFAULT NOW(); DECLARE v_serial_no VARCHAR(100) DEFAULT ''; DECLARE v_step INT DEFAULT 0; SELECT t.serial_no INTO v_serial_no FROM sys_job_log t WHERE t.object_name = 'p_order_pool_sync' AND t.start_time >= CURRENT_DATE() ORDER BY t.log_id DESC LIMIT 1; IF v_serial_no IS NULL OR v_serial_no = '' THEN SET v_serial_no = CONCAT(v_obj_name, ':', REPLACE(UUID(), '-', '')); INSERT INTO `sys_job_log`(`object_name`, start_time, `serial_no`) VALUES (v_obj_name, v_start_time, v_serial_no); END IF; BEGIN #异常处理段 DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN SET v_error = TRUE; GET DIAGNOSTICS CONDITION 1 r_code = RETURNED_SQLSTATE , r_msg = MESSAGE_TEXT; END; SET v_step = v_step + 1; CALL p_order_goods_sync(); SET v_step = v_step + 1; CALL p_order_recharge_sync(); SET v_step = v_step + 1; CALL p_order_crp_coupon_sync(); END; -- 同示例一 UPDATE `sys_job_log` t SET t.`status` = IF(v_error, 90, 0), t.`end_time` = NOW(), t.`duration_time` = TIMESTAMPDIFF(SECOND, v_start_time, NOW()), t.`result_msg` = CONCAT(v_step, '-', r_code, '|', r_msg) WHERE t.`serial_no` = v_serial_no AND t.`object_name` = v_obj_name; END ;; DELIMITER ;
关于事件启停,状态的一些SQL:
SHOW VARIABLES LIKE 'event_scheduler';
SHOW GLOBAL VARIABLES LIKE "event_scheduler";
SET GLOBAL event_scheduler=1;
SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST;
SELECT * FROM wx_message_task;
SHOW ERRORS;
SHOW CREATE event exp_account_sync;
SHOW EVENTS;
-- 开启某事件:
ALTER EVENT e_test ON COMPLETION PRESERVE ENABLE;
-- 关闭某事件:
ALTER EVENT e_test ON COMPLETION PRESERVE DISABLE;