缘起
mysql数据库中有用到分表,大概300张表,都是以geo_data_xxxxxx
来命名,之前测试过程中,有给300张表中的一个字段设置过值,但是想重新生成一遍数据。那么后面是做了一个数据清洗,给300张表is_turnout
字段设置成null
值。若要一个一个表update,工作量也确实有点大了。
可以用mysql的存储过程来实现。
操作
大致思路:
- 先查到
geo_data_xxxxxx
的所有表的表名
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '库名' AND TABLE_NAME REGEXP 'geo_data_[0-9]'
- 遍历循环,执行update即可
update 表名 set is_turnout = null
好像挺完美。
完整语句如下:
DROP PROCEDURE IF EXISTS set_geo_turnout_null;
CREATE PROCEDURE set_geo_turnout_null()
BEGIN
-- 该变量用于标识是否还有数据需遍历
DECLARE flag INT DEFAULT 0;
-- 创建一个变量用来存储遍历过程中的值
DECLARE tbl_name VARCHAR(100);
-- 查询出需要遍历的数据集合
DECLARE tbl_list CURSOR FOR (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '库名' AND TABLE_NAME REGEXP 'geo_data_[0-9]');
-- 查询是否有下一个数据,没有将标识设为1,相当于hasNext
DECLARE CONTINUE HANDLER FOR NOT FOUND SET flag = 1;
-- 打开游标
OPEN tbl_list;
-- 取值设置到临时变量中
FETCH tbl_list INTO tbl_name;
-- 遍历未结束就一直执行
WHILE flag != 1 DO
-- targetSQL //你想要执行的目标功能,这里可以写多个SQL
update tbl_name set is_turnout = null;
-- 一定要记得把游标向后移一位
FETCH tbl_list INTO tbl_name;
END WHILE;
-- 关闭游标
CLOSE tbl_list;
END;
欧克,信心满满,执行完然后速度调用,嘿嘿
CALL set_geo_turnout_null();
嘎?
什么玩意儿啊,不讲武德的,这都能错吗??
排错
首先从问题来看,就是执行update
的时候,我的变量tbl_name
它没有被解析出来,而是被当成了一个字符串。
后面又想着,既然表名是一个变量,那么我可以进行拼接吗,update concat('geo_data_','111111')
,执行了一手,真不行吧。
经过查阅资料,可以使用mysql的动态sql来实现,类似如下:
SET @table_name = 'your_table_name';
SET @sql = CONCAT('UPDATE ', @table_name, ' SET column1 = value1, column2 = value2 WHERE condition');
PREPARE stmt FROM @sql;
EXECUTE stmt;
欧克,赶紧尝试一手。
DROP PROCEDURE IF EXISTS set_geo_turnout_null;
CREATE PROCEDURE set_geo_turnout_null()
BEGIN
-- 该变量用于标识是否还有数据需遍历
DECLARE flag INT DEFAULT 0;
-- 创建一个变量用来存储遍历过程中的值
DECLARE tbl_name VARCHAR(100);
-- 查询出需要遍历的数据集合
DECLARE tbl_list CURSOR FOR (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '库名' AND TABLE_NAME REGEXP 'geo_data_[0-9]');
-- 查询是否有下一个数据,没有将标识设为1,相当于hasNext
DECLARE CONTINUE HANDLER FOR NOT FOUND SET flag = 1;
-- 打开游标
OPEN tbl_list;
-- 取值设置到临时变量中
FETCH tbl_list INTO tbl_name;
-- 遍历未结束就一直执行
WHILE flag != 1 DO
-- targetSQL //你想要执行的目标功能,这里可以写多个SQL
SET @sql = CONCAT('UPDATE ', tbl_name, ' SET is_turnout = null');
PREPARE stmt FROM @sql;
EXECUTE stmt;
-- 一定要记得把游标向后移一位,这个坑我替各位踩过了,不需要再踩了
FETCH tbl_list INTO tbl_name;
END WHILE;
CLOSE tbl_list;
END;
保存,调用…成功了~~
优化+日志
笔者也是深受封装思想的影响,见到乱七八糟的东西,就想给它打包带走。
也就是一个存储过程给拆解成两个就行了。那么我可以将update
的动态sql封装起来,设置为一个存储过程;然后循环表设置字段值又是一个存储过程,最后再调用下update存储过程就行了。
update动态sql
,接收一个table_name的入参
DROP PROCEDURE IF EXISTS update_table_geo;
CREATE PROCEDURE update_table_geo(IN table_name VARCHAR(255))
BEGIN
-- 日志
select table_name;
-- 动态sql语句
SET @sql = CONCAT('UPDATE ', table_name, ' SET is_turnout = null');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
循环处理存储过程
DROP PROCEDURE IF EXISTS set_geo_turnout_null;
CREATE PROCEDURE set_geo_turnout_null()
BEGIN
-- 该变量用于标识是否还有数据需遍历
DECLARE flag INT DEFAULT 0;
-- 创建一个变量用来存储遍历过程中的值
DECLARE tbl_name VARCHAR(100);
-- 查询出需要遍历的数据集合
DECLARE tbl_list CURSOR FOR (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '库名' AND TABLE_NAME REGEXP 'geo_data_[0-9]');
-- 查询是否有下一个数据,没有将标识设为1,相当于hasNext
DECLARE CONTINUE HANDLER FOR NOT FOUND SET flag = 1;
-- 打开游标
OPEN tbl_list;
-- 取值设置到临时变量中
FETCH tbl_list INTO tbl_name;
-- 遍历未结束就一直执行
WHILE flag != 1 DO
-- targetSQL //你想要执行的目标功能,这里可以写多个SQL
-- 执行 update 动态sql
call update_table_geo(tbl_name);
-- 一定要记得把游标向后移一位
FETCH tbl_list INTO tbl_name;
END WHILE;
CLOSE tbl_list;
END;