由于还在开发阶段,每次测试完之后需要清理数据库表,然后待清理的表数量较多,且有些基础数据表需要保留,有些表(如用户表)仅保留部分记录,由于有个发邮件的定时器,还得保证清理数据的顺序。所以尝试使用存储过程来实现一键清理数据的功能,仅以此过程分享给大家
思路:
- 首先是创建名为clearMaterialDatabase的函数,暂时保留一个入参isKeepMaterial来控制是否保留材料信息。
- 通过MySQL中自带的信息数据库INFORMATION_SCHEMA可以获取指定数据库的基本信息,包括表名、权限和数据类型等。在通过
CONCAT()
函数拼接指定数据库中各个表表名称,比如CONCAT('DELETE FROM',' wz_input')
,结果就成了一个删除语句DELETE FROM wz_input
,将这些语句组成一个结果集 - 通过遍历这个结果集,执行指定的SQL语句,来达到批量删除的目的。对于需要特殊处理的表,在用流程控制语句
IF ELSEIF
来处理,利用预处理API(EXECUTE stmt)执行相应的SQL语句
存储过程源码:
CREATE DEFINER=`root`@`localhost` PROCEDURE `clearMaterialDatabase`(IN `isKeepMaterial` VARCHAR(255))
BEGIN
#Routine body goes here...
#定义控制流程
DECLARE i INT;
#定义拼接的sql语句
DECLARE strClear VARCHAR(256);
DECLARE done INT DEFAULT 0;
#定义游标
DECLARE curOne CURSOR FOR SELECT CONCAT('TRUNCATE TABLE ',TABLE_NAME,';') as ClearTable
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'material_wz1702';
#绑定控制变量到游标,游标循环结束自动转为True
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
#打开游标
OPEN curOne;
#进入循环
FETCH curOne INTO strClear;
repeat_label: REPEAT
FETCH curOne INTO strClear;
#SELECT strClear;
#开始控制流程IF-ELSEIF
#是否保留材料信息
IF (isKeepMaterial AND ((strClear LIKE '%tc_material;')OR(strClear LIKE '%tc_material_detail;')OR(strClear LIKE '%tc_material_subject;'))) THEN
ITERATE repeat_label;
#默认保留管理员账号
ELSEIF(strClear LIKE '%sys_user;') THEN
SET @mysql = "DELETE from sys_user WHERE login_name not like 'admin'";
PREPARE stmt from @mysql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
ITERATE repeat_label;
#默认保留数据字典/默认保留菜单字典/默认保留主页配置/默认保留角色信息/默认保留合同模板/最后清理邮件记录,防止清理过程中重复发送
ELSEIF((strClear LIKE '%sys_dict;') OR (strClear LIKE '%sys_menu;') OR (strClear LIKE '%sys_config;')OR(strClear LIKE '%sys_role;')OR(strClear LIKE '%wz_contract_template')OR(strClear LIKE '%wz_bid_mail;')) THEN
ITERATE repeat_label;
#默认保留到项目层级
ELSEIF(strClear LIKE '%sys_office;') THEN
SET @mysql = "DELETE FROM sys_office WHERE id > ( SELECT id FROM ( SELECT id FROM sys_office WHERE NAME = '项目级' AND del_flag = 0 ) targetId )";
PREPARE stmt from @mysql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
ITERATE repeat_label;
#结束控制流程
END IF;
#动态执行SQL语句
SET @mysql = strClear;
PREPARE stmt from @mysql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
UNTIL done = 1 END REPEAT;
#关闭游标
CLOSE curOne;
#最后清理邮件记录
SET @mysql = "TRUNCATE TABLE wz_bid_mail;";
PREPARE stmt from @mysql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
过程准备:
1.MySQL预处理语句使用示例:
例一:通过文本字符拼接一个预处理语句
#创建一个函数求(X^2+Y^2)的平方根:
#?表示与参数进行绑定,?不需要用引号引用,相当于在prepare的时候就把sql写死
PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS result';
SET @X = 3;
SET @Y = 4;
EXECUTE stmt1 USING @X @Y;
输出:
result
5
例二:将文本语句当做一个变量
#创建一个函数求(X^2+Y^2)的平方根:
SET@sqlText = 'SELECT SQRT(POW(?,2)) + POW((?,2)) AS result ';
#将sqlText作为一个包含语句文本的变量放入预备语句stmt2中,表明prepare之前可以用@param变量的形式注入sql语句
PREPARE stmt2 FROM @sqlText;
SET @X = 6;
SET @Y = 8;
EXECUTE stmt2 USING @X,@Y;
输出:
result
10
注:
- 该文本必须展现一个单一的SQL语句,而不是多个语句
- ‘?'字符不应加引号,即使您想要把它们与字符串值结合在一起,也不要加引号。参数制作符只能被用于数据值应该出现的地方,不用于SQL关键词和标识符等
- 参数值只能有用户变量提供,USING子句必须准确地指明用户变量。用户变量的数目与语句中的参数制造符的数量一样多。
- 以下SQL语句可以被用在预制语句中:CREATE TABLE, DELETE, DO, INSERT, REPLACE, SELECT, SET, UPDATE和多数的SHOW语句。目前不支持其它语句。
本章参考文档:理解Mysql prepare预处理语句
2.基于Navicat 12.0创建MySQL存储过程的例子:
- 首先进入函数向导:
Navicat 12.0功能区
->函数
->新建函数
->过程
如何需要设置存储过程的参数,可以在这快速设置,如这里传入两个入参X和Y,类型为INT,出参R,类型为INT,所以向导如图所示:(注:这里只是提供图形化的参数设置,也可以跳过直接在存储过程中添加)