【问题标题】:Executing a stored procedure with cursor in PHP在 PHP 中使用游标执行存储过程
【发布时间】:2016-10-25 05:28:42
【问题描述】:

我有一个试图从我的 php.ini 调用的存储过程。这是存储过程:

BEGIN
DECLARE done INT DEFAULT FALSE;
declare phone_temp VARCHAR(20) default '';
declare phone_cur cursor for SELECT DISTINCT sentNum FROM Queue;
declare continue handler for not found set done = true;

#create temp table
create temporary table if not exists temp_return AS SELECT * FROM Queue LIMIT 0;
#empty if exists
delete from temp_return;

open phone_cur;

phone_loop: LOOP
    fetch phone_cur into phone_temp;
    if done then
        leave phone_loop;
    end if;

   insert into temp_return SELECT * FROM Queue WHERE num2=phone_temp LIMIT 2;
   insert into temp_return SELECT * FROM Queue WHERE num1=phone_temp LIMIT 1;
end loop phone_loop;
close phone_cur;

select * from temp_return;

drop table if exists temp_return;
END

直接在mysql workbench中,调用就可以了。在 php 中,它不起作用。这是我的php:

function grabFromSmsQueue(){
    global $myStmt, $conn;
    if(isset($myStmt)){
        $myStmt -> execute();
    }
    else{
        $query = "CALL myStoredProc();";
        $myStmt = $conn->stmt_init();
        $myStmt -> prepare($query);
        $myStmt -> execute();

    }
    $result = $myStmt -> get_result();
    //print_r ($result);
    $info = [];
    if(isset($result)){
        while($data = $result->fetch_assoc()){
            $info[] = $data;
        }
    }
    return $info;
}

像这样连接,我得到以下错误

The localhost page isn’t working
localhost didn’t send any data.
ERR_EMPTY_RESPONSE

我将我的问题追溯到$data = $result->fetch_assoc() 的问题,因为当我将其注释掉并放入print_r 时,我得到了实际返回的内容,即mysqli_result Object ( [current_field] => 0 [field_count] => 9 [lengths] => [num_rows] => 0 [type] => 1 )。我得出的结论是它不起作用,因为[num_rows] => 0

现在,回到我的存储过程,我删除了所有提到的游标并将其替换为硬编码值,它在工作台和 php 中都有效。我已经验证了通过 php 连接的用户有权限,连接是打开的,并且相同的代码可以执行其他存储过程(那些不包括游标的)。这是否意味着我不能在 php 调用的存储过程中使用游标?有没有光标的替代品?我是否在我的 php 语法中遗漏了一些处理游标的内容?

【问题讨论】:

  • 我很少看到游标实现不代表程序员不了解集合、关系和 sql 的功能。他们搞砸了一个经过微调的性能野兽,把它变成了一个笨拙、虚弱、缓慢的混乱。
  • @Drew 如果您指的是我使用游标执行 2 个看似相同的查询,这些查询可以在 1 行中轻松完成,我想我应该澄清一下,我删除了我的复杂查询并将它们替换为这些简单的用来展示我的问题。据我了解,游标可让您单步执行查询返回的值,并一次在循环中使用这些值。我弄错了吗?
  • 你没看错。使用游标始终是“我应该使用游标还是不应该使用游标”之间最糟糕的选择,如果不是的话。
  • @Drew 有时你别无选择。
  • @Drew 你对不需要使用游标的实现有什么建议吗?我要做的就是选择每个组的前 2 个(一组是具有相同 num1 的条目)

标签: php mysql stored-procedures cursor mysql-variables


【解决方案1】:

基于 3 个分组的聊天讨论,这为测试数据提供了 SQLFiddle(那里的数据不多)。

由于测试数据的滑动窗口是now() 与该数据相关的位置,因此使用以下变量来“冻结”now()。只是为了方便测试和验证输出。

所以,最终放弃它并更改使用它的代码中的 4 个引用(请注意,第 3 组使用它两次)。

now() 变量:

select @theNow:=now();
-- REM OUT the following line. It is used only for testing (as now will chg, your data won't)
select @theNow:='2016-06-23 14:00:00';

查询:

select id,sentNum,message,sentTime,startAtTime,sentByTime,msgType,theGrp from
(   select id,sentNum,message,sentTime,startAtTime,sentByTime,msgType,theGrp,
    if(sentNum!=@lastSentNum,greatest(@sentNumChg:=1,0),least(@sentNumChg:=0,1)) as dummy1,
    if(theGrp!=@lastGrp,greatest(@grpChg:=1,0),least(@grpChg:=0,1)) as dummy2,
    if(@sentNumChg=1 or @grpChg=1,@seqNum:=1,@seqNum:=@seqNum+1) as seqNum,
    @lastSentNum:=sentNum as setLast01,
    @lastGrp:=theGrp as setLast02
    from
    (   -- GROUP 1: sentByTime<=now(), INVITE
        select `id`, `sentNum`, `message`, `sentTime`, `startAtTime`, `sentByTime`, `msgType`, 1 as theGrp
        from SmsQueue
        where sentByTime<=@theNow and msgType='invite'
        UNION ALL
        -- GROUP 2 startAtTime<=now(), BROADCAST
        select `id`, `sentNum`, `message`, `sentTime`, `startAtTime`, `sentByTime`, `msgType`, 2 as theGrp
        from SmsQueue
        where startAtTime<=@theNow and msgType='broadcast'
        UNION ALL
        -- GROUP 3: sentByTime>now() && startAtTime<=now(), INVITE
        select `id`, `sentNum`, `message`, `sentTime`, `startAtTime`, `sentByTime`, `msgType`, 3 as theGrp
        from SmsQueue
        where sentByTime>@theNow and startAtTime<=@theNow and msgType='invite'
    ) d1
    cross join (select @sentNumChg:=0,@grpChg:=0,@lastSentNum:='',@lastGrp:=0,@seqNum:=0) as xParams
    order by sentNum,theGrp,sentByTime,id -- id is the tie-break
) d2
where (theGrp=1 and seqNum<3) or (theGrp=2 and seqNum=1) or (theGrp=3 and seqNum=1)
order by sentNum,theGrp;

输出(我的客户端工具目前受到文本挑战):

在我的this 答案顶部查看我的通用 cmets,了解高级变量用法。

【讨论】:

  • 德鲁,你是国王,这太壮观了!太感谢了!救生员!
  • 我只是一个初级宫廷小丑。周围的其他国王。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多