关于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)的更多相关文章

  1. oracle执行带输入输入参数的存储过程

    declare a1 ); a2 ); begin PKG_INPATIENT.prc_autojf('Y', a1, a2); end;

  2. 本地动态SQL

    (转自:http://blog.itpub.net/26622598/viewspace-718134) 一.什么是动态SQL 大多数PL/SQL都做着一件特殊的结果可预知的工作.例如,一个存储过程可 ...

  3. 存储过程中执行动态Sql语句

    MSSQL为我们提供了两种动态执行SQL语句的命令,分别是EXEC和sp_executesql;通常,sp_executesql则更具有优势,它提供了输入输出接口,而EXEC没有.还有一个最大的好处就 ...

  4. 笔记:MyBatis 动态SQL

    有时候,静态的SQL语句并不能满足应用程序的需求.我们可以根据一些条件,来动态地构建SQL语句.例如,在Web应用程序中,有可能有一些搜索界面,需要输入一个或多个选项,然后根据这些已选择的条件去执行检 ...

  5. T-SQL动态查询(4)——动态SQL

    接上文:T-SQL动态查询(3)--静态SQL 前言: 前面说了很多关于动态查询的内容,本文将介绍使用动态SQL解决动态查询的一些方法. 为什么使用动态SQL: 在很多项目中,动态SQL被广泛使用甚至 ...

  6. 超全MyBatis动态SQL详解!( 看完SQL爽多了)

    MyBatis 令人喜欢的一大特性就是动态 SQL. 在使用 JDBC 的过程中, 根据条件进行 SQL 的拼接是很麻烦且很容易出错的. MyBatis 动态 SQL 的出现, 解决了这个麻烦. My ...

  7. SQL Server创建存储过程——动态SQL

    简介: 存储过程(stored procedure)是一组为了完成特定功能的SQL语句集合,经编译后存储在服务器端的数据库中,利用存储过程可以加速SQL语句的执行. 自定义存储过程,由用户创建并能完成 ...

  8. 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 ...

  9. 怎样SQL存储过程中执行动态SQL语句

    MSSQL为我们提供了两种动态执行SQL语句的命令,分别是EXEC和sp_executesql;通常,sp_executesql则更具有优势,它提供了输入输出接口,而EXEC没有.还有一个最大的好处就 ...

随机推荐

  1. [ch05-02] 用神经网络解决多变量线性回归问题

    系列博客,原文在笔者所维护的github上:https://aka.ms/beginnerAI, 点击star加星不要吝啬,星越多笔者越努力 5.2 神经网络解法 与单特征值的线性回归问题类似,多变量 ...

  2. python数据挖掘第一篇:正则表达式

    正则表达式 re 模块 re.match(pattern,string[,flag]) match方法 从首字母开始匹配,如果包含pattern字符串,则匹配成功,返回match对象,失败则返回Non ...

  3. 2019CCPC秦皇岛 J MUV LUV EXTRA(KMP)

    MUV LUV EXTRA Time Limit: 2000/1500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)T ...

  4. Mybatis代码生成器Mybatis-Generator使用详解

    前提 最近在做创业项目的时候因为有比较多的新需求,需要频繁基于DDL生成Mybatis适合的实体.Mapper接口和映射文件.其中,代码生成器是MyBatis Generator(MBG),用到了My ...

  5. idea实现简单热部署

    首先我们打开设置tomcat的页面         

  6. 通信协议TLV的介绍及在python下的代码实现及仿真

    TLV协议是一种通讯协议,一般将数据封装成TLV的形式,即Tag,Length,Value.协议就是指通信双方对数据传输控制的一种规定,规定了数据格式,同步方式,传送速度,传送步骤的问题作出统一的规定 ...

  7. 介绍一款自己实现的rabbit轻量级组件和使用方法

    DotNetCore.RabbitMQ.Extensions介绍 这是一个 基于.NETStandard 2.0的Rabbit轻量级框架,可以让开发人员无需关注底层变动,专注编写业务代码,从而达到便捷 ...

  8. flask之分析线程和协程

    flask之分析线程和协程 01 思考:每个请求之间的关系 我们每一个请求进来的时候都开一个进程肯定不合理,那么如果每一个请求进来都是串行的,那么根本实现不了并发,所以我们假定每一个请求进来使用的是线 ...

  9. Unity中文API参考手册

    转载请标明原文地址:http://www.cnblogs.com/zhangyukof/p/6835582.html  Unity5中文脚本手册 网页版 Unity API 执行顺序: Unity5中 ...

  10. python 多线程编程之threading模块(Thread类)创建线程的三种方法

    摘录 python核心编程 上节介绍的thread模块,是不支持守护线程的.当主线程退出的时候,所有的子线程都将终止,不管他们是否仍在工作. 本节开始,我们开始介绍python的另外多线程模块thre ...