2

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

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

任何人都可以通过传入应该受影响的数据库的名称来想办法在多个数据库中使用相同的存储过程吗?

4

5 回答 5

8

Vijay Jadhav的答案是 MySQL 解决此限制的正确方法。实际上,您需要 3 个 proc 来完成它:

proc1 使用 Vijay Jadhav 的方式,像数据收集器一样工作。您需要将变量传递给 proc1 并让它为 proc2 创建 tmp 表。Vijay 的方式有一个限制,他应该使用“CREATE TEMPORARY TABLE tmp_table_name SELECT ...”创建一个 TEMPORARY 表。因为临时表是线程安全的。

proc2 在 proc1 创建的 tmp 表上声明游标。由于 tmp 表是已知的并且硬编码到声明中,因此不再出现“找不到表”错误。

proc3 像“main”函数一样工作,所有参数都需要发送到 proc1 和 proc2。proc3 只是先调用 proc1,然后使用每个 proc 所需的参数调用 proc2。

ps 需要将系统变量“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$$
于 2011-04-05T15:16:53.600 回答
2

不,你不能在游标中这样做。也许只是准备好的陈述可以完成这项工作?:

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"); 
于 2009-11-05T15:44:33.963 回答
2

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

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

...

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

于 2010-01-15T07:15:34.103 回答
0

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

于 2009-11-10T21:13:55.737 回答
0

create procedure test(in dbName varchar(40)) READS SQL DATA <- 此行返回将允许您浏览结果 begin ... $result = call test("mysql");

于 2014-12-02T16:11:05.963 回答