今天写存储过程时,遇到要将表名最为参数的问题,如果不涉及到游标的话,使用prepare可以解决问题,但是,动态表名要运用在游标中的话,则prepare就得靠边站了。
集众人之智慧,最后,使用临时表解决了问题。
如何在MySQL的存储过程中实现把过程参数用在游标定义的SELECT命令里面作为表名引用
首先,我们来把场景描绘一下,比如下面的例子(当然是无法正确运行的):
-
CREATE PROCEDURE `proc`(SourceDBName CHAR(50), SourceTableName CHAR(50), -
TargetDBName CHAR(50), TargetTemplateTableName CHAR(50)) -
BEGIN -
DECLARE done INT DEFAULT 0; -
DECLARE FieldValue CHAR(50); -
DECLARE CursorSegment CURSOR FOR SELECT ... FROM SourceDBName.SourceTableName; -
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; -
OPEN CursorSegment; -
REPEAT -
FETCH CursorSegment INTO FieldValue; -
IF NOT done THEN -
... -
END IF; -
UNTIL done END REPEAT; -
CLOSE CursorSegment; -
END$$
上面的例子试图通过存储过程的参数传递,向存储过程内部的游标定义传递要SELECT的数据库名称和表名称。但是,这个存储过程在运行时MySQL会提示“SourceDBName.SourceTableName”不存在。也就是说MySQL不会把SourceDBName和SourceTableName两个标识符作为局部变量去解析,而是直接作为表引用。
要解决这个问题,唯一的方法就是把上面这个存储过程分为3个存储过程。对,3个。所以说这是一个比较复杂的解决办法。
第一个存储过程,扮演的是数据收集器的角色。它接收参数传递过来的数据库名和表名,然后把数据SELECT到一个临时表中。需要注意,临时表的最大好处是它是线程安全的。
第二个存储过程,基于第一个存储过程生成的临时表而创建游标,并处理具体的工作。
第三个存储过程,作为一个入口,负责依次调用存储过程1和存储过程2,并提供相应的参数。
三个存储过程综合起来,就得到下面的例子:
-
CREATE PROCEDURE `proc1`(SourceDBName CHAR(50), SourceTableName CHAR(50)) -
BEGIN -
DECLARE SQLStmt TEXT; -
SET SQL_NOTES=0; -
SET @SQLStmt = CONCAT('DROP TEMPORARY TABLE IF EXISTS tmp_table_name'); -
PREPARE Stmt FROM @SQLStmt; -
EXECUTE Stmt; -
DEALLOCATE PREPARE Stmt; -
SET @SQLStmt = CONCAT('CREATE TEMPORARY TABLE tmp_table_name SELECT ... FROM ', -
SourceDBName,'.',SourceTableName,' WHERE ... '); -
PREPARE Stmt FROM @SQLStmt; -
EXECUTE Stmt; -
DEALLOCATE PREPARE Stmt; -
END$$ -
CREATE PROCEDURE `proc2`(TargetDBName CHAR(50), TargetTemplateTableName CHAR(50)) -
BEGIN -
DECLARE done INT DEFAULT 0; -
DECLARE FieldValue CHAR(50); -
DECLARE CursorSegment CURSOR FOR SELECT Period FROM tmp_table_name; -
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; -
OPEN CursorSegment; -
REPEAT -
FETCH CursorSegment INTO FieldValue; -
IF NOT done THEN -
... -
END IF; -
UNTIL done END REPEAT; -
CLOSE CursorSegment; -
END$$ -
CREATE PROCEDURE `proc3`(SourceDBName CHAR(50), SourceTableName CHAR(50), -
TargetDBName CHAR(50), TargetTemplateTableName CHAR(50)) -
BEGIN -
CALL proc1(SourceDBName, SourceTableName); -
CALL proc2(TargetDBName, TargetTemplateTableName); -
END$$
补充:运行前需要把系统参数变量“sql_notes”设置为0,否则proc1在DROP TABLE时会停下来。原因是
-
"SQL_NOTES = {0 | 1} -
If set to 1 (the default), warnings of Note level are recorded. -
If set to 0, Note warnings are suppressed."
本文解决MySQL存储过程中动态表名用于游标SELECT命令的问题,介绍了一种复杂但有效的解决方案,即将任务分解为三个存储过程,分别用于数据收集、基于临时表创建游标及处理工作,确保线程安全。
2532

被折叠的 条评论
为什么被折叠?



