mysql 存儲過程 動態表名

今天寫存儲過程時,遇到要將表名最爲參數的問題,若是不涉及到遊標的話,使用prepare能夠解決問題,可是,動態表名要運用在遊標中的話,則prepare就得靠邊站了。html

集衆人之智慧,最後,使用臨時表解決了問題。sql

如何在MySQL的存儲過程當中實現把過程參數用在遊標定義的SELECT命令裏面做爲表名引用數據庫

首先,咱們來把場景描繪一下,好比下面的例子(固然是沒法正確運行的):安全

  1.  
  2. CREATE PROCEDURE `proc`(SourceDBName CHAR(50), SourceTableName CHAR(50),
  3. TargetDBName CHAR(50), TargetTemplateTableName CHAR(50))
  4. BEGIN
  5. DECLARE done INT DEFAULT 0;
  6. DECLARE FieldValue CHAR(50);
  7. DECLARE CursorSegment CURSOR FOR SELECT ... FROM SourceDBName.SourceTableName;
  8. DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
  9.  
  10. OPEN CursorSegment;
  11. REPEAT
  12. FETCH CursorSegment INTO FieldValue;
  13. IF NOT done THEN
  14. ...
  15. END IF;
  16. UNTIL done END REPEAT;
  17. CLOSE CursorSegment;
  18. END$$

上面的例子試圖經過存儲過程的參數傳遞,向存儲過程內部的遊標定義傳遞要SELECT的數據庫名稱和表名稱。可是,這個存儲過程在運行時MySQL會提示「SourceDBName.SourceTableName」不存在。也就是說MySQL不會把SourceDBName和SourceTableName兩個標識符做爲局部變量去解析,而是直接做爲表引用。spa

要解決這個問題,惟一的方法就是把上面這個存儲過程分爲3個存儲過程。對,3個。因此說這是一個比較複雜的解決辦法。線程

第一個存儲過程,扮演的是數據收集器的角色。它接收參數傳遞過來的數據庫名和表名,而後把數據SELECT到一個臨時表中。須要注意,臨時表的最大好處是它是線程安全的。code

第二個存儲過程,基於第一個存儲過程生成的臨時表而建立遊標,並處理具體的工做。htm

第三個存儲過程,做爲一個入口,負責依次調用存儲過程1和存儲過程2,並提供相應的參數。get

三個存儲過程綜合起來,就獲得下面的例子:it

  1.  
  2. CREATE PROCEDURE `proc1`(SourceDBName CHAR(50), SourceTableName CHAR(50))
  3. BEGIN
  4. DECLARE SQLStmt TEXT;
  5.  
  6. SET SQL_NOTES=0;
  7.  
  8. SET @SQLStmt = CONCAT('DROP TEMPORARY TABLE IF EXISTS tmp_table_name');
  9. PREPARE Stmt FROM @SQLStmt;
  10. EXECUTE Stmt;
  11. DEALLOCATE PREPARE Stmt;
  12.  
  13. SET @SQLStmt = CONCAT('CREATE TEMPORARY TABLE tmp_table_name SELECT ... FROM ',
  14. SourceDBName, '.',SourceTableName,' WHERE ... ');
  15. PREPARE Stmt FROM @SQLStmt;
  16. EXECUTE Stmt;
  17. DEALLOCATE PREPARE Stmt;
  18. END$$
  19.  
  20. CREATE PROCEDURE `proc2`(TargetDBName CHAR(50), TargetTemplateTableName CHAR(50))
  21. BEGIN
  22. DECLARE done INT DEFAULT 0;
  23. DECLARE FieldValue CHAR(50);
  24. DECLARE CursorSegment CURSOR FOR SELECT Period FROM tmp_table_name;
  25. DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
  26.  
  27. OPEN CursorSegment;
  28. REPEAT
  29. FETCH CursorSegment INTO FieldValue;
  30. IF NOT done THEN
  31. ...
  32. END IF;
  33. UNTIL done END REPEAT;
  34. CLOSE CursorSegment;
  35. END$$
  36.  
  37. CREATE PROCEDURE `proc3`(SourceDBName CHAR(50), SourceTableName CHAR(50),
  38. TargetDBName CHAR(50), TargetTemplateTableName CHAR(50))
  39. BEGIN
  40. CALL proc1(SourceDBName, SourceTableName);
  41. CALL proc2(TargetDBName, TargetTemplateTableName);
  42. END$$
  43.  

補充:運行前須要把系統參數變量「sql_notes」設置爲0,不然proc1在DROP TABLE時會停下來。緣由是

  1.  
  2. "SQL_NOTES = {0 | 1}
  3. If set to 1 (the default), warnings of Note level are recorded.
  4. If set to 0, Note warnings are suppressed."
相關文章
相關標籤/搜索