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

发布于 2024-08-10 07:56:09 字数 1132 浏览 2 评论 0原文

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

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

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

寻找我们的幸福 2024-08-17 07:56:09

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

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

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$

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$
私野 2024-08-17 07:56:09

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

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

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"); 
她比我温柔 2024-08-17 07:56:09

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

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

...

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

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.

三寸金莲 2024-08-17 07:56:09

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

The answer to this is that it cannot be done. You cannot use variables in the cursor declaration. I appreciate noonex's response. However, his solution does not allow me to walk through the results. It simply executes the query.

等你爱我 2024-08-17 07:56:09

创建过程测试(在 dbName varchar(40) 中)
读取 SQL 数据 <- 此行返回将允许您浏览结果
开始
...
$结果=调用测试(“mysql”);

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文