MySQL Stored Procedures : Use a variable as the database name in a cursor declaration

22,944

Solution 1

The answer of Vijay Jadhav is the right way to solve this limitation by MySQL. Actually, you need 3 proc to accomplish it:

proc1 using Vijay Jadhav's way, works like a data collector. You need to pass the variables to proc1 and let it create the tmp table for proc2. There is one limiation of Vijay's way, he should create a TEMPORARY table by using "CREATE TEMPORARY TABLE tmp_table_name SELECT ...". Because temporary table is thread safe.

proc2 declare the cursor on the tmp table which is created by proc1. Since the tmp table is already known and hard coded into the declaration, no more "table not found" error.

proc3 works like a "main" function, with all the parameters need to be sent to proc1 and proc2. proc3 simply calls proc1 first and then proc2 with the parameters need by each proc.

p.s Need to set system variable "sql_notes" to 0, otherwise proc1 will stop on DROP TABLE command.

Here is my example:

CREATE PROCEDURE `proc1`(SourceDBName CHAR(50), SourceTableName CHAR(50))
BEGIN
  DECLARE SQLStmt TEXT;

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

Solution 2

Try to create (temporary) table using prepared statement in a different procedure.

SET @query = CONCAT("CREATE TABLE temp_table AS SELECT cdrs_id, called, calling FROM "     ,dbName, ".cdrs WHERE lrn_checked = 'N' ");

...

And then select data from that table in your 'test' procedure.

Solution 3

No, you can't do that in cursors. Maybe just prepared statements may do the job? :

delimiter ;;

create procedure test(in dbName varchar(40))
begin

set @query := CONCAT("SELECT * FROM " , dbName, ".db;");

PREPARE s from @query;

EXECUTE s;
DEALLOCATE PREPARE s;

end;;

delimiter ;

call test("mysql"); 
Share:
22,944
Justin Noel
Author by

Justin Noel

Hi, I'm Justin Noel. I'm a Full Stack/Mobile Developer. I dabble in : Firebase, Ionic Framework, StencilJS, React, React Native, AngualrJS, Angular, Node, PHP, LAMP, Cordova, PhoneGap, MongoDB, MySQL. Twitter: https://twitter.com/calendee Web: https://calendee.com

Updated on December 03, 2020

Comments

  • Justin Noel
    Justin Noel over 3 years

    I need to use a variable to indicate what database to query in the declaration of a cursor. Here is a short snippet of the code :

    CREATE PROCEDURE `update_cdrs_lnp_data`(IN dbName VARCHAR(25), OUT returnCode SMALLINT)
    
    cdr_records:BEGIN
    
    DECLARE cdr_record_cursor CURSOR FOR 
    
     SELECT cdrs_id, called, calling FROM dbName.cdrs WHERE lrn_checked = 'N';
    
     # Setup logging
     DECLARE EXIT HANDLER FOR SQLEXCEPTION
     BEGIN
          #call log_debug('Got exception in update_cdrs_lnp_data');
          SET returnCode = -1;
     END;
    

    As you can see, I'm TRYING to use the variable dbName to indicate in which database the query should occur within. However, MySQL will NOT allow that. I also tried things such as :

    CREATE PROCEDURE `update_cdrs_lnp_data`(IN dbName VARCHAR(25), OUT returnCode SMALLINT)
    
    cdr_records:BEGIN
    
    DECLARE cdr_record_cursor CURSOR FOR 
    
            SET @query = CONCAT("SELECT cdrs_id, called, calling FROM " ,dbName, ".cdrs WHERE lrn_checked = 'N' ");
            PREPARE STMT FROM @query;
            EXECUTE STMT;
    
     # Setup logging
     DECLARE EXIT HANDLER FOR SQLEXCEPTION
     BEGIN
          #call log_debug('Got exception in update_cdrs_lnp_data');
          SET returnCode = -1;
     END;
    

    Of course this doesn't work either as MySQL only allows a standard SQL statement in the cursor declaration.

    Can anyone think of a way to use the same stored procedure in multiple databases by passing in the name of the db that should be affected?

  • Justin Noel
    Justin Noel over 14 years
    noonex - that seems like a good idea. But how do I then iterate through the results?