MySQL 程序的光标在第一次迭代后停止
MySQL procedure's cursor stops after first iteration
我正在编写一个程序来获取数据table 以映射一个字段并 insert/update 映射到另一个 table。
我的问题是,如果映射函数未找到任何匹配项,我的光标将在第一次迭代后停止而不会抛出任何错误。
这是我的函数:
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
通过测试我发现如果在我的映射 table 中有一个相应的字段它工作,但是如果 SELECT returns 一个 NULL 值因为没有映射字段是找到,它将在第一次迭代时停止光标。
然后我在另一台服务器上的另一个数据库上尝试了它,一切正常,所以可能是配置问题?两者都有字符集“latin1 -- cp1252 West European”排序规则“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;
如果名称映射正确,此过程将正确插入,但如果名称未映射,它将在第一行之后停止。
你可以用下面的tables
来测试一下
-- ----------------------------
-- 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');
在 MySQL 5.6 之前,存储过程只有一个处理程序,请参阅 changelogs for 5.6:
In addition, several deficiencies in condition handler processing rules were corrected so that MySQL behavior is more like standard SQL:
- Block scope is used in determining which handler to select. Previously, a stored program was treated as having a single scope for handler selection.
所以你的 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 及更高版本,您当前的代码将按预期工作。
我遇到了完全相同的问题,但公认的解决方案对我没有帮助。我们有一个很旧的 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 ;
我正在编写一个程序来获取数据table 以映射一个字段并 insert/update 映射到另一个 table。
我的问题是,如果映射函数未找到任何匹配项,我的光标将在第一次迭代后停止而不会抛出任何错误。
这是我的函数:
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
通过测试我发现如果在我的映射 table 中有一个相应的字段它工作,但是如果 SELECT returns 一个 NULL 值因为没有映射字段是找到,它将在第一次迭代时停止光标。
然后我在另一台服务器上的另一个数据库上尝试了它,一切正常,所以可能是配置问题?两者都有字符集“latin1 -- cp1252 West European”排序规则“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;
如果名称映射正确,此过程将正确插入,但如果名称未映射,它将在第一行之后停止。
你可以用下面的tables
来测试一下-- ----------------------------
-- 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');
在 MySQL 5.6 之前,存储过程只有一个处理程序,请参阅 changelogs for 5.6:
In addition, several deficiencies in condition handler processing rules were corrected so that MySQL behavior is more like standard SQL:
- Block scope is used in determining which handler to select. Previously, a stored program was treated as having a single scope for handler selection.
所以你的 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 及更高版本,您当前的代码将按预期工作。
我遇到了完全相同的问题,但公认的解决方案对我没有帮助。我们有一个很旧的 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 ;