【问题标题】:Syntax error while looping through all rows of a table in a stored procedure循环遍历存储过程中表的所有行时出现语法错误
【发布时间】:2014-01-02 10:34:53
【问题描述】:

在此问题How can I loop through all rows of a table? (MySQL) 的已接受答案中,发布了以下代码:

INSERT INTO table_B(ID, VAL) VALUES(ID, VAL) FROM table_A LIMIT i,1;

我想先从 table_A 中选择变量,这样我可以像这样重用它:

SELECT VAL FROM table_A INTO variableVal LIMIT i, 1;
INSERT INTO table_B(ID, VAL) VALUES(ID, variableVal);

但这给了我一个语法错误:

#1064 - 您的 SQL 语法有错误;检查手册 对应于您的 MySQL 服务器版本,以便使用正确的语法 靠近'LIMIT i, 1;

这是完整的代码

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;
CREATE PROCEDURE ROWPERROW()
BEGIN
  DECLARE i INT DEFAULT 0;
  DECLARE factionCount INT DEFAULT 0;
  DECLARE factionName varchar(100);
  SELECT COUNT(*) FROM faction INTO factionCount;

  SET i = 0;
  WHILE i < factionCount DO   
    SELECT name FROM faction INTO factionName LIMIT i, 1;
    //SELECT name FROM faction LIMIT i, 1 INTO factionName; (doesn't work either)

    INSERT INTO groups_group(name) values (factionName);

    //going to do something else with factionName
    SET i = i + 1;
  END WHILE;
End;
;;

call ROWPERROW();

【问题讨论】:

  • 你想用factionName做什么?很可能你可以在没有循环和游标的情况下做到这一点。

标签: mysql sql stored-procedures cursor


【解决方案1】:

您的直接错误是由于您的SELECT INTO 语法错误造成的。 INTO 子句应该放在 FROM 之前。

您的过程的语法正确版本可能看起来像

DELIMITER $$
CREATE PROCEDURE ROWPERROW()
BEGIN
  DECLARE i INT DEFAULT 0;
  DECLARE factionCount INT DEFAULT 0;
  DECLARE factionName varchar(100);
  SELECT COUNT(*) INTO factionCount FROM faction ;

  SET i = 0;
  WHILE i < factionCount DO   
    SELECT name INTO factionName FROM faction LIMIT i, 1;
    INSERT INTO groups_group(name) VALUES (factionName);

    -- going to do something else with factionName
    SET i = i + 1;
  END WHILE;
END$$
DELIMITER ;

这里是SQLFiddle演示

现在,即使它在技术上可行且有效,我强烈建议您不要以这种方式处理您的数据。

  1. 根本不要使用LOOP。如果另一个会话在您的程序运行时删除了几行,您的代码将会中断。

  2. 如果您希望逐行处理,至少使用游标。

  3. 如果您可以使用数据集方法(并且在大多数情况下可以)表达您的处理,请远离游标。


带有光标的版本可能看起来像

DELIMITER$$
CREATE PROCEDURE ROWPERROW2()
BEGIN
  DECLARE done INT DEFAULT 0;
  DECLARE factionName varchar(100);
  DECLARE cursor1 CURSOR FOR SELECT name FROM faction;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

  OPEN cursor1;

  read_loop: LOOP
    FETCH cursor1 INTO factionName;
    IF done THEN
        LEAVE read_loop;
    END IF;
    INSERT INTO groups_group(name) VALUES (factionName);

    -- going to do something else with factionName
  END LOOP;

  CLOSE cursor1;
END$$
DELIMITER ;

这里是SQLFiddle演示

【讨论】:

  • 第一个解决方案没用,你以为是别人说的MySQL版本的问题?我会研究游标,性能不是问题,因为它只是一次性的事情。它适用于在服务器停止时升级 Minecraft 插件版本的人。
  • 您使用的是什么版本的 MySQL?就像我说的那样,您无论如何都不想使用LOOP。这与性能无关,而与常识有关。尝试使用我提供的游标的工作示例,或者如果您需要基于集合的方法的帮助,请解释您的实际目标。
  • 这个答案有帮助吗?
  • 这很有帮助,但它让我意识到循环和游标过度杀伤。我发现我可以在一系列查询中做我想做的事。谢谢
【解决方案2】:

您不能将变量与 LIMIT 一起使用。要遍历行,请改用 CURSOR。

尝试查看您是否使用 SQL 的基于集合的方法(使用普通 SQL 语句)。使用循环/游标应该是最后的手段,因为使用普通 SQL 命令通常可以获得更好的性能。

【讨论】:

  • 可以LIMIT子句中使用变量(存储块级和用户(会话)级)。
  • 这取决于 MySQL 版本。 Limit 仅接受来自 MySQL 5.5.6 的变量。根据错误消息 Jonny 正在运行旧版本。
【解决方案3】:

你需要把@放在变量之前。

【讨论】:

  • @'s 给了我语法错误,但我认为我不需要将它们放在那里,因为没有它们它也可以工作。不要认为缺少 @ 是导致当前语法错误的原因吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-11
  • 1970-01-01
相关资源
最近更新 更多