【问题标题】:Stored procedure with a cursor executes in strange manner带有游标的存储过程以奇怪的方式执行
【发布时间】:2012-11-27 14:32:55
【问题描述】:

我有一个存储过程,它应该使用游标处理表中的行。该过程大部分时间都有效,但有时它不能完全执行。我知道这一点是因为我有一个简单的调试工具,嵌入到代码中,它将特定的行和变量记录到专用的调试表中。最有趣的是,从 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 点。

似乎发生了一些内部错误,但我不确定如何捕获它。奇怪的是,这有时会发生在相同的数据上,否则会起作用。

附: MySQL 版本 5.0.51b,PHP 5.2.6。

附言我设法找到了一个相关的问题 - Calling a Stored Procedure Within a Cursor Loop, Without Tripping the Continue Handler。正如我的过程的名称所暗示的那样,它是从外部过程的循环内部调用的(顺便说一句,它有一个通过“路径”表的循环和另一个继续处理程序),所以它类似于那些情况,并且可能是有点重要。我已经尝试了链接问题的解决方案,但它也没有帮助。

找到了解决办法,答案贴在下面

【问题讨论】:

    标签: php mysql stored-procedures


    【解决方案1】:

    停止循环的处理程序,如:

      DECLARE CONTINUE HANDLER FOR NOT FOUND SET stop = TRUE;
    

    对整个过程是全局的。

    因此,它很可能会捕获其他未找到的事件,而不是来自游标获取,并导致循环提前退出。

    尝试在光标获取周围使用一个开始结束块just,并在那里声明一个处理程序。

    【讨论】:

    • 感谢您的想法,但可惜没有帮助。我发现了一个可能与此相关的错误:bugs.mysql.com/bug.php?id=12168(标记为已修复并在其他条件下显示)。我打算找到一种解决方法。
    【解决方案2】:

    我已经解决了这个问题,虽然它的完成方式对我来说似乎很奇怪。

    线条:

    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's控制执行流程。

    似乎存储过程不喜欢 SELECTed 到(不可用)控制台。在我的情况下,这导致了不一致的行为:在相同数据上的某些任意测试运行中,程序放弃了执行。如果有人能阐明这种行为,我很乐意接受这个答案,而不是我的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-17
      • 2015-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多