今天寫存儲過程時,遇到要將表名最爲參數的問題,若是不涉及到遊標的話,使用prepare能夠解決問題,可是,動態表名要運用在遊標中的話,則prepare就得靠邊站了。html
集衆人之智慧,最後,使用臨時表解決了問題。sql
如何在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兩個標識符做爲局部變量去解析,而是直接做爲表引用。spa
要解決這個問題,惟一的方法就是把上面這個存儲過程分爲3個存儲過程。對,3個。因此說這是一個比較複雜的解決辦法。線程
第一個存儲過程,扮演的是數據收集器的角色。它接收參數傳遞過來的數據庫名和表名,而後把數據SELECT到一個臨時表中。須要注意,臨時表的最大好處是它是線程安全的。code
第二個存儲過程,基於第一個存儲過程生成的臨時表而建立遊標,並處理具體的工做。htm
第三個存儲過程,做爲一個入口,負責依次調用存儲過程1和存儲過程2,並提供相應的參數。get
三個存儲過程綜合起來,就獲得下面的例子:it
-
-
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."