今天寫存儲過程時,遇到要將表名最為參數的問題,如果不涉及到游標的話,使用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."