Разрешение многопользовательского PHP-запроса продолжаться при возникновении ошибок
Я уже что-то спрашивал о перехвате ошибок в мультизапросе в PHP, но никто не ответил. Теперь я немного погуглил и немного понял проблему.
Я получил этот код:
// Some starting variables
$test = new mysqli("localhost","root","","testdatabase");
$Query = array();
$Query[] = "SELECT 'wos' FROM items WHERE itemID = 3";
$Query[] = "SELECT 'wos' FROM items WHERE itemID";
$Query[] = "SELECT 'wos' FROM items WHERE itemID = 5";
$Query = implode(";",$Query);
$Errors = array();
// Execute multi query
if ($test->multi_query($Query)) {
do {
// fetch results
$Result = $test->store_result();
print_r($Result);
if($test->errno === 0) {
echo $Result->num_rows . "<br>";
}
else {
$Errors[] = $test->error;
}
$Result->close();
if (!$test->more_results()) {
break;
}
if (!$test->next_result()) {
$Errors[] = $test->error;
break;
}
} while (true);
}
Как видите, у меня есть несколько X последующих запросов, и я хочу выполнить их все. Поэтому, если запрос 2 завершится неудачно, я хочу продолжить несколько запросов, чтобы запустить следующие запросы. Как я могу это сделать?
Кроме того, если возникает ошибка, я хочу сохранить эту ошибку в $Errors
, Однако в настоящее время он работает не совсем так, как задумано, поскольку я могу сохранить только сообщение об ошибке, но не соответствующий запрос. Только сообщение об ошибке не очень полезно без запроса, который не удался. Поэтому я хочу иметь что-то вроде: "Query 3 failed ('SELECT ...'): 'ERROR MESSAGE'"
,
Как это возможно? Я могу получить текущую ошибку в цикле с $test->error
, но у меня нет ничего подобного $test->currentsubsequentquery
, Как я могу это сделать?
Большое спасибо.
2 ответа
Проблема здесь заключается в том, что можно назвать ограничением в протоколе MySQL. При использовании мультизапроса (с точки зрения MySQL: для параметра "Несколько операторов" задано значение "Вкл.", А затем для отправки запроса) сервер разделяет это и выполняет их один за другим. Ответы будут отправлены без ссылки на источник. Единственный способ, которым клиент (в данном случае PHP/mysqli) может выяснить, к какому фактическому утверждению относится ошибка, - это проанализировать полностью строку запроса и попытаться выполнить какое-либо сопоставление. Но это не сработает во многих случаях - некоторые заявления, в первую очередь CALL
, может вернуть несколько результирующих наборов, которые не могут быть сопоставлены с оператором.
Если вы хотите узнать больше об ошибке, есть два варианта:
Уродливый делает $Query = implode(";\n\n",$Query);
а затем проанализируйте сообщение об ошибке для предоставленной строки и сопоставьте их.
Или просто не используйте multi_query. multi_query предназначен для обработки вызовов хранимых процедур, возвращающих несколько наборов результатов. Использование multi_query в случае, подобном описанному выше, сэкономит вам лишь небольшую задержку, поскольку сервер может напрямую обрабатывать следующий запрос, не ожидая следующей команды запроса, но даже этого можно достичь с помощью асинхронных операций запроса mysqlnd.
Ну, несколько запросов в одном утверждении, как правило, не очень хорошая идея. Хорошие клиенты SQL (например, PDO) вообще не допускают множественных запросов, потому что таким образом внедрение SQL значительно усложняется. Я хотел бы отговорить вас от вашей идеи и предложить одно из следующих решений:
- Используйте три отдельных утверждения
- Поскольку ваши три запроса очень похожи, вы должны использовать подготовленный оператор и выполнить его три раза с разными параметрами привязки. Это обеспечивает лучшую производительность, чем три отдельных оператора, и делает внедрение SQL невозможным.
Вы можете создать такой общий запрос или сделать что-нибудь с UNION:
"SELECT 'wos' FROM items WHERE itemID IN ('3', '5')"
Если вы сейчас думаете: "О чем говорит этот парень? Это больше не будет атомным". тогда вы должны использовать транзакцию...