当我在一大块代码中遇到问题并且找不到问题所在时,我通常将我的代码分成更小的块并一次测试它们:
测试 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;
查看INSERT 和INSERT ... 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 ;
您可以使用相同的方法来调试许多复杂的编程问题:从您的代码的简单版本开始,对其进行测试。如果它可以使用更多代码再次测试,如果没有找到并修复错误,然后再继续。