使用动态SQL处理table_name作为输入参数的存储过程(MySQL)
关于mysql如何创建和使用存储过程,参考笔记《MySQL存储过程和函数创建》以及官网:https://dev.mysql.com/doc/refman/5.7/en/create-procedure.html
本篇主要示例使用了输入参数的存储过程,并解决使用表名作为输入参数的问题,因为之前遇到过需要使用表名作为参数的存储过程,很难处理。
问题描述:
假设我们有TEST1-TEST12共12个相同结构的车辆里程表,我们想要对这12个表进行去重,那么逻辑上比较简单的办法是写12个存储过程处理或者写一个存储过程每执行一次改一次表名并重新编译,但是这样都太麻烦了。
接下来很容易的就会想到是否可以使用表名作为输入参数,这样每次执行给定表名即可。
因此初始的存储过程代码如下:
DELIMITER //
DROP PROCEDURE IF EXISTS Del_Dupilicate;
CREATE DEFINER=`root`@`localhost` PROCEDURE `Del_Dupilicate`(in table_name varchar(64))
BEGIN
DECLARE v_min_id,v_group_count INT;
DECLARE v_get_on_time,v_get_off_time DATETIME;
DECLARE v_car_no VARCHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE my_cur CURSOR FOR SELECT get_on_time,get_off_time,car_no,min(id),count(1) AS count FROM table_name GROUP BY get_on_time,get_off_time,car_no HAVING count>1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN my_cur;
myloop: LOOP
FETCH my_cur INTO v_get_on_time,v_get_off_time,v_car_no,v_min_id,v_group_count;
IF done THEN
LEAVE myloop;
END IF;
DELETE FROM table_name WHERE get_on_time=v_get_on_time AND get_off_time=v_get_off_time AND car_no=v_car_no AND id>v_min_id;
COMMIT;
END LOOP;
CLOSE my_cur;
END;
//
DELIMITER ;
上述存储过程可以正常编译,但是执行却一定会报table not exist的错误,因为mysql会错误的把输入变量table_name当做真正的数据库表名,这显然是错误的。
那么如何在SQL中引用变量呢?一个可行的办法是使用动态SQL,把变量拼入SQL语句中然后执行动态SQL。
所以根据官网(https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html)提供的语法,对于上述procedure中的delete语句可以改写成如下格式:
set @del_sql=concat('DELETE FROM ',table_name,' WHERE get_on_time=',v_get_on_time,' AND get_off_time=',v_get_off_time,' AND car_no=',v_car_no,' AND id>',v_min_id)
PREPARE stmt FROM @del_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
//注意prepare目前只能在存储过程中使用,函数和触发器都不适用。
Ps:需要注意的是官网在示例prepare的语法时使用了?作为占位符,但是经试验?不能作为表名的占位符(实际上官网只示例了?可以作为整数字面量的占位符,我猜测凡是数据库对象用?作为占位符都会报错),想要将表名变量整合入SQL中只能使用concat函数,concat的函数的输入支持local variables、user defined variables和input variables。
好,delete语句处理完毕,但是对于cursor中的select语句呢?官网明确说明游标中不能使用动态SQL,也就是不能使用prepare语句,那只能换一种思路了。
游标的作用是什么呢?是获取一个结果集以便进行遍历,那么可否使用临时表代替游标来存储结果集,这样可以使用动态SQL创建临时表(mysql的临时表是session级别的,不同会话可以使用相同名称的临时表,会话释放时临时表自动删除):
set @tmp_table_name=concat(table_name,'_tmp');
set @cur_sql=concat('create temporary table ',@tmp_table_name,' as select get_on_time,get_off_time,car_no,min(id) as min_id,count(1) AS count FROM ',table_name,' GROUP BY get_on_time,get_off_time,car_no HAVING count>1');
PREPARE stmt FROM @cur_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
然后整个存储过程的逻辑就可以更改了,因为我们把中间结果集存入了临时表,那就无需遍历cursor了,同时连declare的local variables也省了(因为这些本地变量是用于遍历游标时存储列值的),只需要delete ... join即可,因此最终的存储过程修改为:
CREATE DEFINER=`root`@`localhost` PROCEDURE `Del_Dupilicate`(in table_name varchar(64))
BEGIN set @tmp_table_name=concat(table_name,'_tmp'); set @cur_sql=concat('create temporary table ',@tmp_table_name,' as select get_on_time,get_off_time,car_no,min(id) as min_id,count(1) AS count FROM ',table_name,' GROUP BY get_on_time,get_off_time,car_no HAVING count>1');
PREPARE stmt FROM @cur_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt; set @del_sql=concat('delete a from ',table_name,' a join ',@tmp_table_name,' b on a.get_on_time=b.get_on_time and a.get_off_time=b.get_off_time and a.car_no=b.car_no and a.id != b.min_id');
PREPARE stmt FROM @del_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt; set @drop_tmp_sql=concat('drop temporary table ',@tmp_table_name);
PREPARE stmt FROM @drop_tmp_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt; END
调用:
call Del_Dupilicate('TEST1');
上述存储过程经过了实测,可以正常的删除重复数据。
使用动态SQL处理table_name作为输入参数的存储过程(MySQL)的更多相关文章
- oracle执行带输入输入参数的存储过程
declare a1 ); a2 ); begin PKG_INPATIENT.prc_autojf('Y', a1, a2); end;
- 本地动态SQL
(转自:http://blog.itpub.net/26622598/viewspace-718134) 一.什么是动态SQL 大多数PL/SQL都做着一件特殊的结果可预知的工作.例如,一个存储过程可 ...
- 存储过程中执行动态Sql语句
MSSQL为我们提供了两种动态执行SQL语句的命令,分别是EXEC和sp_executesql;通常,sp_executesql则更具有优势,它提供了输入输出接口,而EXEC没有.还有一个最大的好处就 ...
- 笔记:MyBatis 动态SQL
有时候,静态的SQL语句并不能满足应用程序的需求.我们可以根据一些条件,来动态地构建SQL语句.例如,在Web应用程序中,有可能有一些搜索界面,需要输入一个或多个选项,然后根据这些已选择的条件去执行检 ...
- T-SQL动态查询(4)——动态SQL
接上文:T-SQL动态查询(3)--静态SQL 前言: 前面说了很多关于动态查询的内容,本文将介绍使用动态SQL解决动态查询的一些方法. 为什么使用动态SQL: 在很多项目中,动态SQL被广泛使用甚至 ...
- 超全MyBatis动态SQL详解!( 看完SQL爽多了)
MyBatis 令人喜欢的一大特性就是动态 SQL. 在使用 JDBC 的过程中, 根据条件进行 SQL 的拼接是很麻烦且很容易出错的. MyBatis 动态 SQL 的出现, 解决了这个麻烦. My ...
- SQL Server创建存储过程——动态SQL
简介: 存储过程(stored procedure)是一组为了完成特定功能的SQL语句集合,经编译后存储在服务器端的数据库中,利用存储过程可以加速SQL语句的执行. 自定义存储过程,由用户创建并能完成 ...
- MyBatis动态SQL(认真看看, 以后写SQL就爽多了)
目录 0 一起来学习 mybatis 1 数据准备 2 if 标签 2.1 在 WHERE 条件中使用 if 标签 2.1.1 查询条件 2.1.2 动态 SQL 2.1.3 测试 2.2 在 UPD ...
- 怎样SQL存储过程中执行动态SQL语句
MSSQL为我们提供了两种动态执行SQL语句的命令,分别是EXEC和sp_executesql;通常,sp_executesql则更具有优势,它提供了输入输出接口,而EXEC没有.还有一个最大的好处就 ...
随机推荐
- 记录我的 python 学习历程-Day05 字典/字典的嵌套
一.字典的初识 为什么要有字典 字典与列表同属容器型数据类型,同样可以存储大量的数据,但是,列表的数据关联性不强,并且查询速度比较慢,只能按照顺序存储. 什么是字典 先说一下什么叫可变与不可变的数据类 ...
- nginx编译安装配置模块大全
使用configure命令配置构建.它定义了系统的各个方面,包括允许nginx用于连接处理的方法.最后,它会创建一个Makefile.该configure命令支持以下参数:--help 打印帮助信息. ...
- 【Leetcode 做题学算法周刊】第六期
首发于微信公众号<前端成长记>,写于 2019.12.15 背景 本文记录刷题过程中的整个思考过程,以供参考.主要内容涵盖: 题目分析设想 编写代码验证 查阅他人解法 思考总结 目录 11 ...
- zabbix4.0.1 安装部署
zabbix安装部署 目录 一.环境准备... 3 1.1.版本:... 3 1.2.部署环境... 3 二.安装部署... 3 2.1.zabbix安装... 3 2.1.1.下载zabbix的rp ...
- Python 电路绘制库 schemdraw 你会吗?【面试必学】
前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:skyztttt 引子 由于最近在学习ardunio的使用,其中牵涉到绘 ...
- linux-history、find、
1.history:查看历史记录 -c:清除历史命令记录 -d:删除某一条使用过的命令,-d后跟命令的序列号 2.find:在目录结构中搜索文件 -type:后面跟文件的类型,d表示目录,f表示文件 ...
- 微信团队分享:极致优化,iOS版微信编译速度3倍提升的实践总结
1.引言 岁月真是个养猪场,这几年,人胖了,微信代码也翻了. 记得 14 年转岗来微信时,用自己笔记本编译微信工程才十来分钟.如今用公司配的 17 年款 27-inch iMac 编译要接近半小时:偶 ...
- django生命周期请求l流程图
django思维导图链接:https://www.processon.com/view/link/5dddb0f8e4b074c442e5c68c
- 《Dotnet9》系列-开源C# WPF项目1《Accelerider.Windows》强力推荐
时间如流水,只能流去不流回! 点赞再看,养成习惯,这是您给我创作的动力! 本文 Dotnet9 https://dotnet9.com 已收录,站长乐于分享dotnet相关技术,比如Winform.W ...
- [ASP.NET Core 3框架揭秘] 文件系统[4]:程序集内嵌文件系统
一个物理文件可以直接作为资源内嵌到编译生成的程序集中.借助于EmbeddedFileProvider,我们可以采用统一的编程方式来读取内嵌的资源文件,该类型定义在 "Microsoft.Ex ...