如何在另一个存储过程(PHP和mysqli)中调用存储过程


How to call a stored procedure within another stored procedure (PHP and mysqli)

我很难在另一个存储过程中调用存储过程。就像现在一样,存储过程永远不会将SELECT语句作为结果返回到PHP中的mysqli调用(但它在MySQL工作台中运行良好)。

DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `new_bid`(IN bid_in decimal(6,2),
                                                      IN ticker_in varchar(5),
                                                      IN share_amount_in BIGINT)
BEGIN
    DECLARE company_id_var INT;
    DECLARE highest_bid BIT;
    DECLARE generated_bid_id BIGINT;
    SET company_id_var = (SELECT ID
                      FROM Companies
                      WHERE Ticker = ticker_in);
    -- Put the bid into the Bids table.
    INSERT INTO Bids(company_id,bid,share_amount)
    VALUES (company_id_var,bid_in,share_amount_in);
    SET generated_bid_id = LAST_INSERT_ID();
    CALL check_available_shares(bid_in,share_amount_in,generated_bid_id,@shares_left);
    -- Check if the bid is higher than the current highest bid.
    -- If so update the CurrentState table to have the new value.
    UPDATE CurrentState SET buyer = bid_in
                        WHERE ID = company_id_var
                        AND buyer < bid_in;
    IF (ROW_COUNT() = 1)
     THEN
     SELECT 1 AS highest_bid, @shares_left AS shares_left, CS.buyer, CS.seller, CS.last_price, CO.name,  CO.ticker
     FROM CurrentState CS, Companies CO
     WHERE CS.id = CO.id
     AND CO.ticker = ticker_in;
    ELSE
     SELECT 0 AS highest_bid, @shares_left AS shares_left, CS.buyer, CS.seller, CS.last_price, CO.name,  CO.ticker
     FROM CurrentState CS, Companies CO
     WHERE CS.id = CO.id
     AND CO.ticker = ticker_in;
    END IF;
END

如果我从这个存储过程中删除"CALL"函数,它会在底部返回一个SELECT语句,但如果我保留"CALL"函数,它不会返回任何内容。我尝试过exec和execute,但当我试图在MySQL工作台中保存过程时,这只会给我带来语法错误。此问题与https://stackoverflow.com/questions/23193085/mysqli-does-not-return-any-results-but-stored-procedure-does.但这个问题似乎与mysqli如何处理存储过程的执行有关,因为当我在mySQL workbench(?)

中手动调用存储过程时,它运行良好

几个小时后,我的头撞在墙上,找到了解决方案。由于这个存储过程调用另一个使用游标的存储过程,我不得不使用$mysqli->multi_query()函数。以下是我的工作原理:

new_bid存储过程:

-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `new_bid`(IN bid_in decimal(6,2),
                                                      IN ticker_in varchar(5),
                                                      IN share_amount_in BIGINT)
BEGIN
    -- Store the company ID into a variable as this value will used more than once.
    DECLARE company_id_var INT;
    DECLARE ParamtoPass INT;
    DECLARE highest_bid BIT;
    DECLARE generated_bid_id BIGINT;
    SET company_id_var = (SELECT ID
                      FROM Companies
                      WHERE Ticker = ticker_in);
    -- Put the bid into the Bids table.
    INSERT INTO Bids(company_id,bid,share_amount)
    VALUES (company_id_var,bid_in,share_amount_in);
    SET generated_bid_id = LAST_INSERT_ID();
    -- EXECUTE check_available_shares bid_in, @share_amount_in,@generated_bid_id,@shares_left;
    CALL check_available_shares (bid_in,@share_amount_in,generated_bid_id,@shares_left);
    -- Check if the bid is higher than the current highest bid.
    -- If so update the CurrentState table to have the new value.
    UPDATE CurrentState SET buyer = bid_in
                        WHERE ID = company_id_var
                        AND buyer < bid_in;
    IF (ROW_COUNT() = 1)
     THEN
     SELECT 1 AS highest_bid, @shares_left AS shares_left, CS.buyer, CS.seller, CS.last_price, CO.name,  CO.ticker
     FROM CurrentState CS, Companies CO
     WHERE CS.id = CO.id
     AND CO.ticker = ticker_in;
    ELSE
     SELECT 0 AS highest_bid, @shares_left AS shares_left, CS.buyer, CS.seller, CS.last_price, CO.name,  CO.ticker
     FROM CurrentState CS, Companies CO
     WHERE CS.id = CO.id
     AND CO.ticker = ticker_in;
    END IF;
END

check_available_shares过程:

-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `check_available_shares`(IN bid_in decimal(6,2),
                                                                     IN share_amount_in int,
                                                                     IN bid_id bigint,
                                                                     OUT shares_left BIT)
BEGIN
    DECLARE num_shares INT;
    DECLARE selling_id INT;
    DECLARE selling_price DECIMAL(6,2);
    DECLARE done INT DEFAULT FALSE;
    DECLARE cur CURSOR FOR SELECT share_amount,sell_price,ID
                           FROM ShareSell
                           WHERE sell_price <= bid_in
                           ORDER BY sell_price ASC, sell_timestamp ASC;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    OPEN cur;   /* open cursor */
    read_loop: LOOP
        FETCH NEXT FROM cur
                   INTO num_shares, selling_price, selling_id;
        IF (done) THEN
            LEAVE read_loop;
        END IF;
        IF ((share_amount_in - num_shares) > 0) THEN
            SET share_amount_in = (share_amount_in - num_shares);
            DELETE FROM ShareSell
                   WHERE ID = selling_id;
        ELSEIF((share_amount_in - num_shares) < 0) THEN
            UPDATE ShareSell
            SET share_amount = (share_amount - share_amount_in)
            WHERE ID = selling_id;
            DELETE FROM Bids
            WHERE ID = bid_id;
            LEAVE read_loop;
        ELSE
            DELETE FROM ShareSell
                   WHERE ID = selling_id;
            DELETE FROM Bids
                   WHERE ID = bid_id;
        END IF;
        SELECT num_shares,selling_price;
    END LOOP;
    CLOSE cur;
    IF (share_amount_in > 0) THEN
        UPDATE Bids
        SET share_amount = share_amount_in
        WHERE ID = bid_id;
        SET shares_left = 1;
    ELSE
        SET shares_left = 0;
    END IF;
END

最后是PHP调用:

$bid = 43.10;
$ticker = "GOS";
$share_amount = 1000;
if ($mysqli->multi_query("CALL new_bid($bid,'$ticker',$share_amount)"))
{
 if ($result = $mysqli->store_result())
 {
   while ($row = $result->fetch_assoc())
   {
     print_r($row);
   }
   $result->close();
 }
}

谢谢!这也正是我的问题。我也在一个完全类似的问题上崩溃了(请参阅PHP mysqli在调用存储过程时没有捕获一些错误)。你的帖子帮我解决了问题!

我也有一个PHP调用SP1的例子,而SP1又调用SP2,在mysql客户端中一切都很好,但当被PHP调用时就会崩溃!事实上,我在SP2中也有一个结果集的SELECT。我的问题症状和你的完全一样,我从来没有想过要使用multi_query()

我注意到您在check_available_shares中也选择了一个"结果集"(请参见第SELECT num_shares,selling_price;行)。不确定您为什么有这一行。如果您删除了那一行,我怀疑您应该能够执行query()而不是multi_query()