【问题标题】:End while loop missing semicolon结束while循环缺少分号
【发布时间】:2016-11-25 10:46:44
【问题描述】:

我创建了一个简单的存储过程,它遍历一个表的行并将它们插入到另一个表中。出于某种原因,END WHILE 循环抛出了缺少分号的错误。所有代码对我来说都是正确的,所有分隔符都设置正确。我只是不知道为什么会抛出这些错误,谷歌搜索这个问题只会指出我使用不正确的分隔符答案,但仅此而已。任何帮助都会很好!

USE test;
DELIMITER $$

DROP PROCEDURE IF EXISTS `testLoop`$$

CREATE PROCEDURE `testLoop`()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT COUNT(*) FROM `test_dropship_upload` INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO `test_2` (sku, qty) VALUES(sku, qty) FROM `test_dropship_upload` LIMIT i,1;
  SET i = i + 1;
END WHILE;

END $$

DELIMITER ;

【问题讨论】:

    标签: mysql stored-procedures while-loop


    【解决方案1】:

    当我在一大块代码中遇到问题并且找不到问题所在时,我通常将我的代码分成更小的块并一次测试它们:

    测试 1:

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `testLoop`$$
    
    CREATE PROCEDURE testLoop()
    BEGIN
    END $$
    
    DELIMITER ;
    

    没有错误:过程声明和分隔符的使用都可以。

    测试 2:

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `testLoop`$$
    
    CREATE PROCEDURE `testLoop`()
    BEGIN
    DECLARE n INT DEFAULT 0;
    DECLARE i INT DEFAULT 0;
    END $$
    
    DELIMITER ;
    

    没有错误:过程中的变量声明是可以的。

    测试 3:

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `testLoop`$$
    
    CREATE PROCEDURE `testLoop`()
    BEGIN
    DECLARE n INT DEFAULT 0;
    DECLARE i INT DEFAULT 0;
    SELECT COUNT(*) FROM `test_dropship_upload` INTO n;
    SET i=0;
    END $$
    
    DELIMITER ;
    

    没有错误:SELECT 查询和变量赋值正常。

    测试 4:

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `testLoop`$$
    
    CREATE PROCEDURE `testLoop`()
    BEGIN
    DECLARE n INT DEFAULT 0;
    DECLARE i INT DEFAULT 0;
    SELECT COUNT(*) FROM `test_dropship_upload` INTO n;
    SET i=0;
    WHILE i<n DO 
      SET i = i + 1;
    END WHILE;
    
    END $$
    
    DELIMITER ;
    

    没有错误:WHILE 循环正常。

    测试 5:

    现在唯一未测试的部分是 INSERT 查询:

    INSERT INTO `test_2` (sku, qty) VALUES(sku, qty) FROM `test_dropship_upload` LIMIT i,1;
    

    查看INSERTINSERT ... SELECT 的文档,我们可以看到您的查询无效:它显然缺少SELECT 部分,如果您想插入则不应该有VALUES 部分另一个表中的值:

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `testLoop`$$
    
    CREATE PROCEDURE `testLoop`()
    BEGIN
    DECLARE n INT DEFAULT 0;
    DECLARE i INT DEFAULT 0;
    SELECT COUNT(*) FROM `test_dropship_upload` INTO n;
    SET i=0;
    WHILE i<n DO 
      INSERT INTO `test_2` (sku, qty) SELECT sku, qty FROM `test_dropship_upload` LIMIT i, 1;
      SET i = i + 1;
    END WHILE;
    
    END $$
    
    DELIMITER ;
    

    过程创建现在完成,没有错误。

    测试 6:

    但是,在执行过程时,您会在 SELECT 查询中遇到语法错误:MySQL 不接受将 LIMIT 与变量一起使用。
    要使其工作,您需要使用prepared statement

    PREPARE stmt FROM "INSERT INTO `test_2` (sku, qty) SELECT sku, qty FROM `test_dropship_upload` LIMIT ?, 1";
    EXECUTE stmt using @i;
    DEALLOCATE PREPARE stmt;
    

    It is also not allowed to used local variables in prepared statements:

    因为局部变量仅在存储程序期间才在作用域内 执行,在准备好的语句中不允许引用它们 在存储的程序中创建。准备好的语句范围是 当前会话,而不是存储的程序,因此该语句可以是 程序结束后执行,此时变量不会 不再在范围内。例如,SELECT ... INTO local_var 不能 用作准备好的语句。此限制也适用于存储 过程和函数参数。

    要避免此问题,请使用会话变量 @i 而不是局部变量 i

    程序的最终版本

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `testLoop`$$
    
    CREATE PROCEDURE `testLoop`()
    BEGIN
    DECLARE n INT DEFAULT 0;
    SELECT COUNT(*) FROM `test_dropship_upload` INTO n;
    SET @i=0;
    WHILE @i<n DO
      PREPARE stmt FROM "INSERT INTO `test_2`(sku, qty) SELECT sku, qty FROM `test_dropship_upload` LIMIT ?, 1";
      EXECUTE stmt USING @i;
      DEALLOCATE PREPARE stmt;
      SET @i = @i + 1;
    END WHILE;
    
    END $$
    
    DELIMITER ;
    

    您可以使用相同的方法来调试许多复杂的编程问题:从您的代码的简单版本开始,对其进行测试。如果它可以使用更多代码再次测试,如果没有找到并修复错误,然后再继续。

    【讨论】:

      猜你喜欢
      • 2018-01-06
      • 1970-01-01
      • 2014-04-20
      • 2014-10-31
      • 2019-03-15
      • 2016-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多