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

时间:2009-11-05 14:17:29

标签: mysql stored-procedures variables cursor

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

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语句。

有人能想到通过传入应该受影响的数据库名称在多个数据库中使用相同存储过程的方法吗?

5 个答案:

答案 0 :(得分:7)

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然后调用每个proc需要的参数proc2。

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

答案 1 :(得分:2)

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

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

...

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

答案 2 :(得分:1)

不,你不能用游标做到这一点。 也许只是准备好的陈述可以做这个工作? :

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

答案 3 :(得分:0)

答案是无法完成的。您不能在游标声明中使用变量。我很感激noonex的回应。但是,他的解决方案不允许我完成结果。它只是执行查询。

答案 4 :(得分:0)

创建过程测试(在dbName varchar(40)中) READS SQL DATA < - 此行返回将允许您浏览结果 开始 ... $ result = call test(“mysql”);