【问题标题】:Syntax issues with MySQL procedureMySQL过程的语法问题
【发布时间】:2017-08-01 12:53:06
【问题描述】:

我从堆栈溢出中找到了这个过程。我想从所有具有特定列的表中获取特定值的行。 所以:

  • 获取包含该列的所有表
  • 从表中获取列值为 x 的所有行

    @DELIMITER $$
    CREATE PROCEDURE db.selectStuff ()
    BEGIN
      DECLARE a varchar(100);
      DECLARE i CURSOR FOR SELECT DISTINCT TABLE_NAME from INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME="my_column_name";
      OPEN i;
      read_loop: LOOP
        FETCH i INTO crt_table;
        SELECT * FROM crt_table WHERE my_column_name=some_id;
      END LOOP read_loop;
      CLOSE i;
    END$$
    @DELIMITER;
    call selectStuff();
    

我收到了

[Code: 1064, SQL State: 42000] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE i CURSOR FOR SELECT DISTINCT TABLE_NAME from INFORMATION_SCHEMA.COLUMNS ' at line 1DBVisualizer Pro 9.5.7

运行时

选择有效。

更新: 从下面的 cmets 中,我将代码更改为:

@DELIMITER $$
CREATE PROCEDURE selectStuff ()
BEGIN
  DECLARE s VARCHAR(300);
  DECLARE i CURSOR FOR SELECT CONCAT('SELECT * FROM ',c.TABLE_NAME,' where `my_column_name`=1') as s from INFORMATION_SCHEMA.COLUMNS c WHERE c.COLUMN_NAME="my_column_name";
  OPEN i;
  do_stuff: LOOP
    FETCH i INTO s;
    PREPARE run FROM s;
    EXECUTE run;
    DEALLOCATE PREPARE run;
  END LOOP do_stuff;
  CLOSE i;
END$$
@DELIMITER;

call selectStuff();

我遇到了同样的错误。

注意:跑步

SELECT CONCAT('SELECT * FROM ',c.TABLE_NAME,' where `my_column_name`=1') as s from INFORMATION_SCHEMA.COLUMNS c WHERE c.COLUMN_NAME="my_column_name" 

确实提供了正确的 SQL 语句,可以得到我想要的。

更新: 在 dbForge Studio Express 中运行它:

DELIMITER $
CREATE PROCEDURE selectStuff()
BEGIN
  DECLARE done TINYINT DEFAULT 0;
  DECLARE s VARCHAR (3000);
  DECLARE i CURSOR FOR SELECT CONCAT("SELECT * FROM ", c.TABLE_NAME, " where my_column_name=1") as QUERY from INFORMATION_SCHEMA.COLUMNS c WHERE c.COLUMN_NAME="my_column_name";
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
  OPEN i;
  do_stuff: LOOP
    FETCH i INTO s;
    IF done = 1 THEN LEAVE do_stuff;
    END IF;
    PREPARE stmt FROM s;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
  END LOOP do_stuff;
  CLOSE i;
END$
DELIMITER;

#call selectStuff();

我遇到了一些其他错误,但光标部分通过了。

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 's;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
  END LOOP do_stuff;

【问题讨论】:

  • 更改:@DELIMITER $$ by DELIMITER $$@DELIMITER; by DELIMITER ;。避免像你的表那样命名变量(crt_table)并声明该变量。
  • 如果这是您的错误消息,则您的分隔符不起作用。另外:您需要使用动态sql。 SELECT * FROM crt_table 将尝试查找表 crt_table,而不是具有该变量值的表。参见例如this question 基本上和你想做的一样,只是做一个update。或者here 为所有表中的每一列提供一个(虽然有点太复杂了)。
  • @solarflare 你能看一下更新吗?如果我运行单行,我会得到正确的查询,但声明仍然有该错误。我正在使用 DBVisualizer。我认为@DELIMITER 是必要的。
  • 显然对于 dbvisualizer,您必须使用 @DELIMITER $$; 包括 ;(和 @DELIMITER ;$$ 来结束它)。对于您在编辑中的新错误:您需要将@variable 用于准备好的语句,因此请使用例如set @dsql = s; PREPARE stmt FROM @dsql;。乍一看,您还应该在查询中包含一个模式(或者在来自 information_schema 的那个中,或者在您的动态 sql 中,添加使用表所在的模式)。最后一个DELIMITER; 必须包含一个空格 (DELIMITER ;)
  • 谢谢,@Solarflare,我已根据您的帮助和关于 stackoverflow 的另一个问题发布了答案。

标签: mysql stored-procedures


【解决方案1】:

好的,所以回答我自己的问题。

感谢 @Solarflare 提供非常有用的链接。

通过遵循此答案here,我能够获得上述问题中的最后一个更新。 似乎错误是由于PREPARE语句不能引用局部变量这一事实引起的,如答案here中所述

在存储程序上下文中准备的语句不能引用存储过程或函数参数或局部变量,因为它们在程序结束时超出范围,并且如果稍后在程序之外执行该语句,它们将不可用。作为一种解决方法,请改为参考用户定义的变量,这些变量也具有会话范围;

THUS,最后的代码是这样的:

DELIMITER $
DROP PROCEDURE IF EXISTS myName;
CREATE PROCEDURE myName(IN id INT)
BEGIN
  DECLARE s VARCHAR(255);
  DECLARE cont TINYINT DEFAULT 1;
  DECLARE crs CURSOR FOR SELECT CONCAT("SELECT * FROM `", c.TABLE_NAME, "` where `my_column`=",id) as Q from INFORMATION_SCHEMA.COLUMNS c WHERE c.COLUMN_NAME="my_column";
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET cont=0;
  SET @sys_var = ''; 
  #THIS is where we declare a user defined variable to 
  #which we pass the actual string we want to execute as sql
  OPEN crs;
  WHILE cont=1 DO
    FETCH crs INTO s;
    SET @sys_var =s; 
    #SELECT @sys_var as ''; #this is just to test if the strings are correct
    PREPARE stmt FROM @sys_var;   
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
  END WHILE;
  CLOSE crs;
END$
DELIMITER;

请注意我仍然如何将光标提取到varchar,但随后我还将变量sys_var 设置为具有相同的值,然后将其传递给PREPARE 我也改用了WHILE DO,因为它对我来说更容易使用,没有其他原因。

注意:我还添加了一个参数,但它与问题无关。

【讨论】:

    猜你喜欢
    • 2016-10-28
    • 2020-03-08
    • 2014-10-11
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多