我有一个存储过程,它应该使用游标处理表中的行。该过程大部分时间都有效,但有时它不会完全执行。我知道这是因为我有一个简单的调试工具,嵌入到代码中,它将特定的行和变量记录到专用的调试表中。最有趣的是,从PHP运行时总会出现问题。如果我使用mysql客户端,我从来没有遇到过这个问题。
程序(以稍微缩短的方式呈现)如下:
CREATE PROCEDURE findnextedge(IN lastid BIGINT)
findnext_context:BEGIN
DECLARE stop BOOLEAN DEFAULT FALSE;
DECLARE count INT DEFAULT 0;
DECLARE cur_fid BIGINT DEFAULT 0;
DECLARE cur_pid1 BIGINT DEFAULT 0;
DECLARE cur_pid2 BIGINT DEFAULT 0;
DECLARE cur CURSOR FOR SELECT fid, pid1, pid2 FROM edges WHERE pid1 = lastid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET stop = TRUE;
CALL debuglog(0, 'findnextedge', 'lastid', lastid, NULL, NULL, NULL, NULL);
SELECT SQL_CALC_FOUND_ROWS fid FROM edges WHERE pid1 = lastid;
SET count = FOUND_ROWS();
CALL debuglog(1, 'findnextedge', 'count', count, NULL, NULL, NULL, NULL);
IF count = 0 THEN
DELETE FROM paths WHERE pid1 = lastid AND pid2 = 0;
SELECT COUNT(*) INTO count FROM paths WHERE pid2 = 0;
CALL debuglog(2, 'findnextedge', 'count', count, NULL, NULL, NULL, NULL);
IF count = 0 THEN
SET @count = 1;
END IF;
LEAVE findnext_context;
END IF;
DELETE FROM paths WHERE pid1 = lastid AND pid2 = 0 ORDER BY pid1 LIMIT 1;
OPEN cur;
CALL debuglog(6, 'findnextedge', 'open', TRUE, NULL, NULL, NULL, NULL);
REPEAT
FETCH cur INTO cur_fid, cur_pid1, cur_pid2;
CALL debuglog(7, 'findnextedge', 'stop', stop, NULL, NULL, NULL, NULL);
IF stop = FALSE THEN
CALL debuglog(3, 'findnextedge', 'cur_fid', cur_fid, 'cur_pid1', cur_pid1, 'cur_pid2', cur_pid2);
// DO MAIN JOB
// ...
CALL debuglog(5, 'findnextedge', NULL, NULL, NULL, NULL, NULL, NULL);
END IF;
CALL debuglog(8, 'findnextedge', 'stop', stop, NULL, NULL, NULL, NULL);
UNTIL stop = TRUE
END REPEAT;
CLOSE cur;
END;
如果出现问题,则会产生整个输出:
point context name1 value1 name2 value2 name3 value3 counter time
0 findnext lastid 0 NULL NULL NULL NULL 0 2012-11-27 18:29:56
1 findnext count 1 NULL NULL NULL NULL 1 2012-11-27 18:29:56
6 findnext open 1 NULL NULL NULL NULL 2 2012-11-27 18:29:56
7 findnext stop 0 NULL NULL NULL NULL 3 2012-11-27 18:29:56
根据日志,在第7点,刚刚获取光标stop
后的值为false,但执行时既没有到达第3点,也没有达到8。
看起来发生了一些内部错误,但我不确定如何捕获它。奇怪的是,这种情况会发生在相同的数据上,而且会不时发生。
P.S。 MySQL版本5.0.51b,PHP 5.2.6。
P.S.S。我设法找到了一个相关的问题 - Calling a Stored Procedure Within a Cursor Loop, Without Tripping the Continue Handler。正如我的过程的名称所暗示的那样,它从外部过程中的循环内部调用(顺便说一下,循环通过“paths”表,另一个是继续处理程序),因此它类似于那些情况,并且可能是不知何故重要。我已经尝试了链接问题的解决方案,但它也没有帮助。
找到解决方案,答案发布在下面。
答案 0 :(得分:1)
停止循环的处理程序,如:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET stop = TRUE;
是整个程序的全局。
因此,它可能会捕获其他未找到的事件,而不是来自光标提取,并导致循环提前退出。
尝试在游标提取周围使用开始结束块 ,并在那里声明一个处理程序。
答案 1 :(得分:0)
我已经解决了这个问题,虽然它的完成方式对我来说似乎很奇怪。
行:
SELECT SQL_CALC_FOUND_ROWS fid FROM edges WHERE pid1 = lastid;
SET count = FOUND_ROWS();
替换为简化:
SELECT COUNT(*) INTO count FROM edges WHERE pid1 = lastid;
这没有任何缺陷。
最初在2行中编码相同内容的原因是我在开头没有调试表,并且通过SELECT
控制执行流程。
似乎存储过程不喜欢将某些内容SELECT
编入(不可用)控制台。在我的情况下,这导致了不一致的行为:过程在相同数据的某些任意测试运行中丢弃执行。如果有人可以阐明这种行为,我很乐意接受这个答案,而不是我的答案。