mysql 存儲過程 動態表名


今天寫存儲過程時,遇到要將表名最為參數的問題,如果不涉及到游標的話,使用prepare可以解決問題,但是,動態表名要運用在游標中的話,則prepare就得靠邊站了。

集眾人之智慧,最后,使用臨時表解決了問題。

如何在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兩個標識符作為局部變量去解析,而是直接作為表引用。

要解決這個問題,唯一的方法就是把上面這個存儲過程分為3個存儲過程。對,3個。所以說這是一個比較復雜的解決辦法。

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

第二個存儲過程,基於第一個存儲過程生成的臨時表而創建游標,並處理具體的工作。

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

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

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



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM