MySQL程序的游标在第一次迭代后停止

时间:2016-11-09 14:17:18

标签: mysql sql function stored-procedures cursor

我正在编写一个过程来获取数据表以映射字段并在另一个表中插入/更新。

我的问题是,如果映射函数找不到任何匹配项,我的游标将在第一次迭代后停止而不会抛出任何错误。

这是我的功能:

BEGIN
    DECLARE mapped_name VARCHAR(255);   

    SELECT mapped_field INTO mapped_name
        FROM mapping_civility
        WHERE original_field = nameVar
    LIMIT 1;

    IF mapped_name IS NULL THEN
        RETURN 'INDEFINI';
    ELSE
        RETURN mapped_name;
    END IF;
END

通过测试,我发现如果我的映射表中有相应的字段,它可以工作,但是如果SELECT返回NULL值,因为没有找到映射字段,它将在第一次迭代时停止光标。

然后我在另一个服务器上的另一个数据库上尝试了,一切都运行正常,所以可能是配置问题?两者都有字符集" latin1 - cp1252西欧"整理" latin1_swedish_ci"。

这是我的程序代码:

    BLOCK1: BEGIN
    DECLARE no_more_rows1 INT;
    DECLARE my_name VARCHAR(255);
    DECLARE civility VARCHAR(255);

    DECLARE curseur1 CURSOR FOR
        SELECT `name`
        FROM source;

    DECLARE CONTINUE handler FOR NOT FOUND SET no_more_rows1 = TRUE;

    OPEN curseur1;
    LOOP1: LOOP
        FETCH curseur1 INTO my_name;
        IF no_more_rows1 THEN
            CLOSE curseur1;
            LEAVE LOOP1;
        END IF;

            SET civility = get_civility(my_name);

            INSERT INTO log (id, message, date) VALUES (NULL, CONCAT(my_name, ' : ', civility), NOW());

    END LOOP LOOP1;     
END BLOCK1;

如果名称映射得很好,此过程将正确插入,但如果未映射名称,它将在第一行后停止。

您可以使用以下表格进行测试

-- ----------------------------
-- Table structure for `source`
-- ----------------------------
DROP TABLE IF EXISTS `source`;
CREATE TABLE `source` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of source
-- ----------------------------
INSERT INTO `source` VALUES ('1', 'Pierre');
INSERT INTO `source` VALUES ('2', 'David');
INSERT INTO `source` VALUES ('3', 'Kevin');
INSERT INTO `source` VALUES ('4', 'Pierre');
INSERT INTO `source` VALUES ('5', 'Donald Pierre');


-- ----------------------------
-- Table structure for `log`
-- ----------------------------
DROP TABLE IF EXISTS `log`;
CREATE TABLE `log` (
  `id` int(5) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `message` text COMMENT 'message',
  `date` varchar(64) DEFAULT NULL COMMENT 'date',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


-- ----------------------------
-- Table structure for `mapping_civility`
-- ----------------------------
DROP TABLE IF EXISTS `mapping_civility`;
CREATE TABLE `mapping_civility` (
  `id` int(5) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `original_field` varchar(255) DEFAULT NULL COMMENT 'original_field',
  `mapped_field` varchar(255) DEFAULT NULL COMMENT 'mapped_field',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of mapping_civility
-- ----------------------------
INSERT INTO `mapping_civility` VALUES ('1', 'kevin', 'H');
INSERT INTO `mapping_civility` VALUES ('2', 'pierre', 'H');
INSERT INTO `mapping_civility` VALUES ('3', 'isabelle', 'F');

2 个答案:

答案 0 :(得分:4)

在MySQL 5.6之前,存储过程只有一个处理程序,请参阅changelogs for 5.6

  

此外,条件处理程序处理规则中的一些缺陷已得到纠正,因此MySQL行为更像标准SQL:

     
      
  • 块范围用于确定要选择的处理程序。以前,存储的程序被视为具有处理程序选择的单个范围。
  •   

因此,您的NOT FOUND继续处理程序将因未在您的函数中找到mapping_civility中的行而触发,因为您在那里使用了into

您可以在获取新行之前直接重新初始化变量,以重置之前发生的所有事情:

...
LOOP1: LOOP
    set no_more_rows1 = false;   -- add this
    FETCH curseur1 INTO my_name;
    IF no_more_rows1 THEN
...

如果您有原始问题中的嵌套循环,请注意它仍然只是一个(活动)处理程序,因此对两个循环使用相同的变量并在每个fetch之前重置它。

对于MySQL 5.6及更高版本,您当前的代码将按预期工作。

答案 1 :(得分:0)

我遇到了完全相同的问题,被接受的解决方案对我没有帮助。我们有一个很旧的MySQL版本,因此我通过创建另一个过程解决了这个问题。内循环过程从外循环获取输入。这是我创建的模板示例:

DELIMITER $$

CREATE PROCEDURE first_procedure ()
BEGIN

    DECLARE v_finished INTEGER DEFAULT 0;
    DECLARE v_some_variable SMALLINT DEFAULT 0;

    DEClARE first_cursor CURSOR FOR 
        select some_variable from some_table; --YOUR QUERY GOES HERE
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = 1;
    OPEN first_cursor;
    get_st_c: LOOP
    set v_finished = 0;
    FETCH first_cursor INTO v_some_variable; -- CAN BE EXTENDED USING COMMAS (,)
    IF v_finished = 1 THEN 
    CLOSE first_cursor;
    LEAVE get_st_c;
    END IF;

        CALL second_procedure(v_some_variable);

    END LOOP get_st_c;

END$$

DELIMITER ;

这是第一步。这将在其循环内调用第二个过程。这是第二个过程:

DELIMITER $$

CREATE PROCEDURE second_procedure (IN passed_variable int)
BEGIN

    DECLARE v_finished INTEGER DEFAULT 0;
    DECLARE v_some_variable_one INT DEFAULT 0;
    DECLARE v_some_variable_two TIMESTAMP DEFAULT now();
    DECLARE v_some_variable_three INT DEFAULT 0;

    -- THIS LOOP IS NESTED BY THE FIRST PROCEDURE'S LOOP
    DEClARE second_cursor CURSOR FOR 
        SELECT some_variable_one, some_variable_two, some_variable_three FROM some_table WHERE variable = passed_variable; -- YOUR QUERY GOES HERE
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = 1;
    OPEN second_cursor;
    get_stc_two: LOOP
    FETCH second_cursor INTO v_some_variable_one, v_some_variable_two, v_some_variable_three;
    IF v_finished = 1 THEN 
    CLOSE second_cursor;
    LEAVE get_stc_two;
    END IF;

        -- YOUR LOGIC GOES HERE

    END LOOP get_stc_two;   

END$$

DELIMITER ;