Разрешение многопользовательского 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')"
    

Если вы сейчас думаете: "О чем говорит этот парень? Это больше не будет атомным". тогда вы должны использовать транзакцию...

Другие вопросы по тегам