MySQL存储过程:在游标声明中使用变量作为数据库名称

l2osamch  于 2023-05-21  发布在  Mysql
关注(0)|答案(5)|浏览(189)

我需要使用一个变量来指示在游标的声明中要查询的数据库。下面是一个简短的代码片段:

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;

如您所见,我尝试使用变量dbName来指示查询应该在哪个数据库中进行。但是MySQL不允许这样做。我还尝试了一些事情,例如:

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;

当然,这也不起作用,因为MySQL只允许在游标声明中使用标准SQL语句。
有没有人能想到一种方法,通过传入应该受到影响的数据库的名称,在多个数据库中使用同一个存储过程?

zbq4xfa0

zbq4xfa01#

Vijay Jadhav的答案是MySQL解决这个限制的正确方法。实际上,你需要3个proc来完成它:
proc1使用Vijay Jadhav的方式,像数据收集器一样工作。您需要将这些变量传递给proc1,让它为proc2创建tmp表。Vijay的方法有一个限制,他应该通过使用“CREATE TEMPORARY TABLE tmp_table_name SELECT ...”创建一个TEMPORARY表。因为临时表是线程安全的。
proc2在proc1创建的tmp表上声明游标。由于tmp表是已知的,并且硬编码到声明中,因此不会再出现“table not found”错误。
proc3的工作方式类似于“main”函数,所有参数都需要发送到proc1和proc2。proc3简单地先调用proc1,然后调用proc2,其中每个proc1都需要参数。
p.s需要将系统变量“sql_notes”设置为0,否则proc1将在DROP TABLE命令时停止。
下面是我的例子:

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

webghufk2#

不,你不能在游标中这样做。也许只是准备好的声明可以做的工作?:

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");
4nkexdtk

4nkexdtk3#

尝试在不同的过程中使用预处理语句创建(临时)表。

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

...
然后在“测试”过程中从该表中选择数据。

oxalkeyp

oxalkeyp4#

对此的回答是:做不到。不能在游标声明中使用变量。我很欣赏noonex的回应。然而,他的解决方案不允许我走过的结果。它只是执行查询。

3phpmpom

3phpmpom5#

create procedure test(in dbName varchar(40))READS SQL DATA<- this line returns will allow you to walk through the results开始...$result = call test(“mysql”);

相关问题