Получение нескольких наборов результатов с помощью хранимой процедуры в php/mysqli


У меня есть хранимая процедура, которая имеет несколько наборов результатов. Как мне перейти ко 2-му набору результатов в mysqli, чтобы получить эти результаты?

Допустим, это сохраненный процесс, например:

create procedure multiples( param1 INT, param2 INT )
BEGIN

SELECT * FROM table1 WHERE id = param1;

SELECT * FROM table2 WHERE id = param2;

END $$

PHP выглядит примерно так:

$stmt = mysqli_prepare($db, 'CALL multiples(?, ?)');

mysqli_stmt_bind_param( $stmt, 'ii', $param1, $param2 );

mysqli_stmt_execute( $stmt );

mysqli_stmt_bind_result( $stmt, $id );

Тогда это та часть, за которую я не могу взяться. Я пытался использовать mysqli_next_result для перехода к следующему набору результатов, но не могу заставить его работать. Мы заставили его работать с mysqli_store_result и mysqli_fetch_assoc/массив/строка, но по какой-то причине все значения int возвращаются в виде пустых строк.

Кто-нибудь еще сталкивался с этим и у кого есть решение?

Author: MacAnthony, 2009-11-06

3 answers

Я думаю, что вы здесь что-то упускаете (следующее не было проверено):

$stmt = mysqli_prepare($db, 'CALL multiples(?, ?)');
mysqli_stmt_bind_param($stmt, 'ii', $param1, $param2);
mysqli_stmt_execute($stmt);
// fetch the first result set
$result1 = mysqli_use_result($db);
// you have to read the result set here 
while ($row = $result1->fetch_assoc()) {
    printf("%d\n", $row['id']);
}
// now we're at the end of our first result set.
mysqli_free_result($result1);

//move to next result set
mysqli_next_result($db);
$result2 = mysqli_use_result($db);
// you have to read the result set here 
while ($row = $result2->fetch_assoc()) {
    printf("%d\n", $row['id']);
}
// now we're at the end of our second result set.
mysqli_free_result($result2);

// close statement
mysqli_stmt_close($stmt);

Использование PDO ваш код будет выглядеть следующим образом:

$stmt = $db->prepare('CALL multiples(:param1, :param2)');
$stmt->execute(array(':param1' => $param1, ':param2' => $param2));
// read first result set
while ($row = $stmt->fetch()) {
    printf("%d\n", $row['id']);
}
$stmt->nextRowset();
// read second result set
while ($row = $stmt->fetch()) {
    printf("%d\n", $row['id']);
}

Но я слышал, что PDOStatement::nextRowset() не реализован с драйвером PDO MySQL, что делает невозможным получение нескольких наборов результатов:

Итак, в зависимости от вашей версии PHP вам придется придерживаться вашего mysqli- решение. Кстати: вы намеренно используете процедурный стиль? Использование объектно-ориентированного стиля с mysqli сделало бы ваш код немного более привлекательным (мое личное мнение).

 13
Author: Stefan Gehrig, 2009-11-06 07:45:11

Похоже, что MySQLi может поддерживать несколько результирующих наборов только через mysqli_multi_query(), поскольку объекты MySQLi_STMT работают иначе, чем объекты MySQLi_Result.

PDO кажется несколько более абстрактным, поскольку объекты PDOStatement способны обрабатывать несколько наборов результатов как для обычных запросов (PDO::query), так и для подготовленных операторов(PDO:prepare).

 1
Author: gapple, 2009-11-05 22:04:57

Это сработало очень хорошо для меня, оно будет иметь дело (в качестве примера) со столькими списками выбора, сколько есть в вашем SP. Обратите внимание, как вам нужно закрыть вызов $, прежде чем вы сможете перейти к параметрам OUT из вашего SP...

?><pre><?
$call = mysqli_prepare($db, 'CALL test_lists(?, ?, @result)');
if($call == false) {
    echo "mysqli_prepare (\$db, 'CALL test_lists(?, ?, @result) FAILED!!!\n";
} else {
    // A couple of example IN parameters for your SP...
    $s_1 = 4;
    $s_2 = "Hello world!";

    // Here we go (safer way of avoiding SQL Injections)...
    mysqli_stmt_bind_param($call, 'is', $s_1, $s_2);

    // Make the call...
    if(mysqli_stmt_execute($call) == false) {
        echo "mysqli_stmt_execute(\$call) FAILED!!!\n";
    } else {
        //print_r($call);

        // Loop until we run out of Recordsets...
        $set = 0;
        while ($recordset = mysqli_stmt_get_result($call)) {
            ++$set;
            //print_r($recordset);
            echo "\nRecordset #" . $set . "...\n";
            if ($recordset->num_rows > 0) {
                $ctr = 0;
                while ($row = $recordset->fetch_assoc()) {
                    ++$ctr;
                    //print_r($row);
                    echo "\t" . $ctr . ": ";
                    forEach($row as $key => $val) {
                        echo "[" . $key . "] " . $val . "\t";
                    }
                    echo "\n";
                }
            }
            echo $recordset->num_rows . " record" . ($recordset->num_rows == 1 ? "" : "s") . ".\n";
            // Clean up, ready for next iteration...
            mysqli_free_result($recordset);

            // See if we can get another Recordset...
            mysqli_stmt_next_result($call);
        }

        // Then you have to close the $call...
        mysqli_stmt_close($call);
        // ...in order to get to the SP's OUT parameters...
        $select = mysqli_query($db, "SELECT @result");
        $row = mysqli_fetch_row($select);
        $result = $row[0];
        echo "\nOUT @result = " . $result . "\n";
    }
}
?></pre><?

И вот как выглядит вывод из приведенного выше кода с использованием моего test_lists SP...

Recordset #1...
    1: [s_1] 4  [user_name] Andrew Foster   
    2: [s_1] 4  [user_name] Cecil   
    3: [s_1] 4  [user_name] Sheff   
3 records.

Recordset #2...
    1: [s_2] Hello world!   [section_description] The Law   
    2: [s_2] Hello world!   [section_description] History   
    3: [s_2] Hello world!   [section_description] Wisdom Literature 
    4: [s_2] Hello world!   [section_description] The Prophets  
    5: [s_2] Hello world!   [section_description] The Life of Jesus and the Early Church    
    6: [s_2] Hello world!   [section_description] Letters from the Apostle Paul 
    7: [s_2] Hello world!   [section_description] Other Letters from Apostles and Prophets  
    8: [s_2] Hello world!   [section_description] Prophecy - warnings for the present and revelation of the future  
8 records.

OUT @result = 16
 0
Author: Andrew Foster, 2018-01-06 15:24:43