Intereting Posts
Как получить весь столбец с именем, имеющим одно и то же имя, при использовании красноречия laravel? Запрет сеанса истек в сеансе PHP для неактивных пользователей Получение данных GET и POST внутри контроллера в Laravel 4 PHP – функция для mysql_fetch_assoc php jquery, как механизм выбора Объективные причины использования пробелов вместо вкладок для отступов? Удалить строку из связанного объекта во многих отношениях во многих отношениях в Doctrine2 Как загрузить файл через Jquery / AJAX Ошибка при отправке файла формы с помощью формы с помощью AJAX Можно ли передать параметр маршрута в конструктор контроллера в Laravel? ob_start ("ob_gzhandler") дает ошибку кодирования содержимого Phalcon PhP – как изменить вид динамически Сброс одиночной кавычки в PHP при вставке в MySQL Где я могу добавить вспомогательные функции Laravel 4, которые могут отображать флэш-сообщения? Скрытие виджетов для определенных пользовательских ролей

2 подготовленных оператора, 2 хранимых процедуры, 1 соединение mysqli

проблема

Как вызвать две хранимые процедуры MySQL в одном и том же соединении mysqli, используя подготовленные операторы (или другой метод запроса, одинаково безопасный для SQL-инъекций) без получения следующих ошибок:

Warning: Packets out of order. Expected 1 received 61. Packet size=7 in /... Warning: mysqli::prepare(): MySQL server has gone away in /... 

Получил код, подключенный к сети через tutorialspoint

История

Я делаю PHP-сервер с базой данных MySQL. У меня есть два результата, которые я хочу получить из одного запроса: список недельных сводок и резюме всех недель.

 ┌───────┬────────────┬────────────┬───── | Week | Sales | Commission | ... ├───────┼────────────┼────────────┼───── | week1 | $7,912.12 | $923.41 | ... | week2 | $6,423.48 | $824.87 | ... | week3 | $8,180.67 | $634.04 | ... | ... | ... | ... | ... ├───────┼────────────┼────────────┼───── | total | $67,012.23 | $7,532.58 | ... | avg | $7,012.54 | $787.38 | ... └───────┴────────────┴────────────┴───── 

Я просто хранили еженедельные сводки в таблице базы данных и использовал хранимую процедуру для получения сводки всех недельных сводок. В моем PHP-коде я просто выбрал все строки в таблице week а затем вызвал хранимую процедуру getWeeksSummary .

Теперь я должен иметь возможность фильтровать данные в еженедельных сводках. Я заменил простой SELECT ... FROM week хранимой процедурой getWeeks() чтобы вычислить все еженедельные сводки.

Код

 $weeksSummary = new stdClass(); if ($stmt = $mysqli->prepare('CALL getWeeks(?,?,?);')) { $stmt->bind_param('sss', $a, $b, $c); $stmt->execute(); $stmt->bind_result($week, $sales, $commission, ...); $weeksSummary->weeks = []; while($stmt->fetch()) { $week = new stdClass(); $week->week = $week; $week->sales = $sales; $week->commission = $commission; ... $weeksSummary->weeks[] = $week; } $stmt->free_result(); $stmt->close(); } if ($stmt = $mysqli->prepare('CALL getWeeksSummary(?,?,?);')) { $stmt->bind_param('sss', $a, $b, $c); $stmt->execute(); $stmt->bind_result($avgSales, $totSales, $avgCommission, $totCommission ...); $stmt->fetch(); $weeksSummary->summary = new stdClass(); $weeksSummary->summary->avgSales = $avgSales; $weeksSummary->summary->totSales = $totSales; $weeksSummary->summary->avgCommission = $avgCommission; $weeksSummary->summary->totCommission = $totCommission; ... $stmt->free_result(); $stmt->close(); } echo json_encode($weeksSummary); в $weeksSummary = new stdClass(); if ($stmt = $mysqli->prepare('CALL getWeeks(?,?,?);')) { $stmt->bind_param('sss', $a, $b, $c); $stmt->execute(); $stmt->bind_result($week, $sales, $commission, ...); $weeksSummary->weeks = []; while($stmt->fetch()) { $week = new stdClass(); $week->week = $week; $week->sales = $sales; $week->commission = $commission; ... $weeksSummary->weeks[] = $week; } $stmt->free_result(); $stmt->close(); } if ($stmt = $mysqli->prepare('CALL getWeeksSummary(?,?,?);')) { $stmt->bind_param('sss', $a, $b, $c); $stmt->execute(); $stmt->bind_result($avgSales, $totSales, $avgCommission, $totCommission ...); $stmt->fetch(); $weeksSummary->summary = new stdClass(); $weeksSummary->summary->avgSales = $avgSales; $weeksSummary->summary->totSales = $totSales; $weeksSummary->summary->avgCommission = $avgCommission; $weeksSummary->summary->totCommission = $totCommission; ... $stmt->free_result(); $stmt->close(); } echo json_encode($weeksSummary); 

Этот код работал нормально, когда первый подготовленный оператор был SELECT week, sales, commission, ... FROM week WHERE a=?, b=?, c=?; вместо CALL getWeeks(?,?,?); , Теперь я получаю эти ошибки:

 Warning: Packets out of order. Expected 1 received 61. Packet size=7 in /... Warning: mysqli::prepare(): MySQL server has gone away in /... 

попытки

1) Ошибка: я использовал новый объект $stmt2 для второго запроса. Те же ошибки.

2) Успех: я закрыл соединение mysqli и открыл новый до второго оператора. Второе соединение mysqli с его собственным подготовленным оператором работает нормально, но код для подключения к базе данных остается полностью отдельным, так что это действительно не помогает.

3) Не удалось: просто из любопытства я вернулся к исходному рабочему коду и переупорядочил инструкции, поставив инструкцию хранимой процедуры перед SELECT . Те же ошибки. Таким образом, соединение mysqli отлично с запросами перед хранимой процедурой, но ничего не нравится после хранимой процедуры.

4) Ошибка: я попытался поставить $mysqli->next_result(); после первого заявления. Те же ошибки. Однако, если я использую query() вместо prepare() для вызова хранимых процедур, next_result() действительно разрешает запуск обеих хранимых процедур. Я бы хотел использовать подготовленный оператор, поскольку они помогают против инъекций SQL.

Нежелательные потенциальные решения

A): я мог бы разделить его на два вызова фонового содержимого, но при обновлении данных резюме не будут синхронизироваться с интерфейсом.

B): я мог бы присоединиться к ним в одну хранимую процедуру MySQL, а затем разделить их на PHP, но мне тоже нужны они отдельно, поэтому один и тот же код будет там дважды.

C): Я мог бы прекратить использование подготовленных операторов, но я не знаю других способов избежать инъекций SQL.

Помогите

Какие-либо предложения?

Ну, я постараюсь ответить за заголовок вопроса , считая, что в первом утверждении не был вызван обычный запрос, но одна из двух вышеупомянутых хранимых процедур .

После вызова хранимой процедуры вам всегда нужно переместить дополнительный пустой набор результатов, возвращаемый каждой хранимой процедурой:

 $mysqli->next_result(); 

Кроме того, после первого вызова функции, добавьте один дополнительный fetch () после получения ваших данных:

 $stmt->fetch(); $stmt->free_result(); 

так как вам нужно «освободить» набор результатов, ожидающий на стороне сервера. Это можно сделать разными способами, но проще всего просто вызвать fetch () еще раз, или, более строго говоря, вам нужно вызвать fetch (), пока не вернет FALSE, указывая, что в наборе результатов больше нет строк , Вы делаете это [молча] в других фрагментах, когда вызываете fetch() в цикле while, но здесь, выбирая только одну строку, вы должны явно ее вызывать.

Есть и другой способ, путь более удобный: используйте get_result() (если доступно), который решит все ваши проблемы сразу. Вместо этого длинного и ветренного кода, который у вас есть на данный момент, нужны только четыре строки:

 $stmt = $mysqli->prepare('CALL getWeeksSummary(?,?,?)'); $stmt->bind_param('sss', $a, $b, $c); $stmt->execute(); $weeksSummary = $stmt->get_result()->fetch_object(); 

get_result() освободит этот ожидающий набор результатов и в то же время позволит вам использовать fetch_object() , который позволит вам получить результирующий объект только в одной строке.

Читая документацию mysqli , говорится, что $stmt->free_result() – освободить память, выделенную из $stmt->store_result() . Поскольку код не использует store_result() , удаление free_result() решает ошибки.

Конечно, он также говорит, что следует использовать store_result() всякий раз, когда запрос возвращает набор результатов. Я действительно не понимаю, почему (что-то делать с буферизацией), но поскольку эти два подготовленных оператора и хранимые процедуры работают без store_result() , проблема решена.

Мне все еще интересно, почему он не работает с store_result() и free_result() , но, по крайней мере, сейчас есть какой-то рабочий код. Вот модифицированный код в tutorialspoint.


В качестве побочного примечания вместо использования двух подготовленных операторов с двумя хранимыми процедурами путь вокруг него заключается в использовании одного подготовленного оператора для установки переменных

 $stmt = $mysqli->prepare('SET @a = ?, @b = ?, @c = ?') ... 

затем используйте эти переменные в запросах для вызова хранимых процедур

 $result = $mysqli->query('CALL getWeeks(@a,@b,@c)') ... $result = $mysqli->query('CALL getWeeksSummary(@a,@b,@c)') ...