2

Используя пример из других источников на SO, чтобы лучше улавливать «скрывающие» ошибки. Хотя приведенный ниже код будет ловить и возвращать ошибку, можно ли улучшить это, чтобы сообщить, для какого запроса возникла ошибка?Как определить запрос, вызвавший ошибку, с помощью mysqli_multi_query?

С ниже кода, выход:

Columns: 18 
Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FRO inventory' at line 1 

Код тестируется:

$query = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20;"; 
$query .= "SELECT * FRO inventory";    // With error 
$ord = array(); 
$invent = array(); 

if(mysqli_multi_query($link, $query)) { 
    do { 
     // fetch results 
     if($result = mysqli_store_result($link)) { 
      echo 'Columns: ' . mysqli_field_count($link) . "<br>"; 
      while($row = mysqli_fetch_assoc($result)) { 
       if(count($row) > 17) 
        $orders[] = $row; 
       elseif(count($row) == 6) 
        $inv[] = $row; 
      } 
     } 
     if(!mysqli_more_results($link)) 
      break; 
     if(!mysqli_next_result($link)) { 
      // report error 
      echo 'Error: ' . mysqli_error($link); 
      break; 
     } 
    } while(true); 
    mysqli_free_result($result); 
} 

ответ

3

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

$q["Orders"]="SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20"; 
$q["Inventory"]="SELECT * FRO inventory"; 

if(!$link=mysqli_connect("host","user","pass","db")){ 
    echo "Failed to connect to MySQL: ",mysqli_connect_error(); 
}elseif(mysqli_multi_query($link,implode(';',$q))){ 
    do{ 
     $q_key=key($q); // current query's key name (Orders or Inventory) 
     if($result=mysqli_store_result($link)){ // if a result set... SELECTs do 
      while($row=mysqli_fetch_assoc($result)){ // if one or more rows, iterate all 
       $rows[$q_key][]=$row; 
      } 
      mysqli_free_result($result); 
      echo "<div><pre>"; // <pre> is for easier array reading 
       var_export($rows[$q_key]); 
      echo "</pre></div>"; 
     } 
    } while(next($q) && mysqli_more_results($link) && mysqli_next_result($link)); 
} 
if($mysqli_error=mysqli_error($link)){ // check & declare variable in same step to avoid duplicate func call 
    echo "<div style=\"color:red;\">Query Key = ",key($q),", Query = ",current($q),", Syntax Error = $mysqli_error</div>"; 
} 

Ошибка при первом запросе: Если ваш первый запрос пытается получить доступ к таблице, которая не существует в выдвинутой базе данных, как: ordersXYZ массива $rows не будет существовать, не var_export() не произойдет, и вы будет видеть этот ответ:

Запрос ключ = заказов, запросов = SELECT * FROM ordersXYZ WHERE расположение = 'В' ORDER BY orderNum DESC LIMIT 20, Ошибка синтаксиса = Таблица '[someDB] .ordersXYZ' не существует

Ошибка на втором запросе: Если ваш первый запрос успешен, но ваш второй запрос пытается получить доступ к несуществующей таблице, как: inventory2
$rows["Orders"] проведет необходимые данные строки и будет var_export() «эд , $row["Inventory"] не будет существовать, и вы увидите этот ответ:

Query Key = Inventory, Query = SELECT * FROM inventory2, Синтаксис Error = Таблица '[someDB] .inventory2' не существует

Нет ошибок: Если оба запроса являются безошибочным, ваш $rows массив будет заполнен нужными данными и var_export() «под ред, и не будет никакого ответа ошибки. С запрошенными данными, сохраненными в $rows, вы можете получить доступ к тем, что вы хотите от $rows["Orders"] и $rows["Inventory"].


Things отметить:

  1. Вы можете заметить, что я делаю объявление переменного и условные проверки в то же время, это делает код более DRY.

  2. Как мой подход использует implode() с точкой с запятой на линии elseif, не забудьте добавить к вашим запросам конечную полуточку.

  3. Этот набор запросов всегда возвращает набор результатов, потому что все являются запросами SELECT, если у вас есть смешанная коллекция запросов, которые affect_rows, вы можете найти полезную информацию по этой ссылке (https://stackoverflow.com/a/22469722/2943403).

  4. mysqli_multi_query() прекратит выполнение запросов, как только появится ошибка. Если вы ожидаете, что поймаете «все» ошибки, вы обнаружите, что их никогда не будет больше одного.

  5. Написание условных точек останова, как в вопросе и решении OP, нецелесообразно. В то время как пользовательские точки останова могут быть правильно использованы в других обстоятельствах, для этого случая точки останова должны быть расположены внутри инструкции while() блока do().

  6. Запрос, возвращающий нулевые строки, не вызовет сообщение об ошибке - он просто не создаст никаких подмассивов в $rows, потому что цикл while() не будет введен.

  7. С помощью функции key() можно избежать условия if/elseif OP, которое подсчитывает столбцы в каждой строке набора результатов. Это лучшая практика, потому что выполнение условий на каждой итерации может стать дорогостоящим в некоторых случаях. Обратите внимание, что указатель массива продвигается внутри $q в конце каждой итерации do(). Это дополнительный метод, который вы не найдете на странице руководства php; он позволяет работать key().

  8. И, конечно, линии <div><pre>var_export()...</pre></div> можно удалить из рабочего кода - это было чисто для демонстрации.

  9. Если вы собираетесь запускать больше запросов после этого кода, чтобы повторно использовать переменные, обязательно очистите все используемые переменные, чтобы остаточные данные не мешали. например $mysqli_error=null; // clear errors & reset($q); // reset array pointer.

  10. Берегись это несколько расплывчатым предупреждения на свое усмотрение: http://php.net/manual/en/mysqli.use-result.php:

    Не следует использовать mysqli_use_result(), если много обработки на стороне клиента выполняется, так как это будет подвязать сервер и предотвращают обновление других потоков от любых таблиц, из которых извлекаются данные .

  11. Наконец и ГЛАВНОГО по соображениям безопасности, не отображать запрос или ошибку запроса информации публично - вы не хотите, зловещие люди, чтобы увидеть этот вид обратной связи. Не менее важно, всегда защищайте свои запросы от инъекционных хаков.Если ваши запросы включают данные, предоставленные пользователем, вам необходимо отфильтровать/дезинформировать данные до смерти, прежде чем использовать их в mysqli_multi_query(). На самом деле, имея дело с пользовательским вводом, моя очень сильная рекомендация - отойти от mysqli_multi_query() и использовать либо prepared statements, либо pdo для взаимодействия с базой данных для более высокого уровня безопасности.

0

в цикле DO, добавить счетчик, каждый успешный mysqli_next_result увеличить счетчик. Когда mysqli_next_result возвращает false, выведите счетчик.

+0

Хороший комментарий. Это не приведет к ошибке в первом запросе. – David

+1

Конечно, вам нужно проверить возвращаемые значения всех функций, что означает mysqli_multi_query и mysqli_store_result для первого запроса, а mysqli_next_result для всех остальных. – rx80

0

Чтобы ответить на мой собственный вопрос, и поскольку документация оставляет желать лучшего, вот решение, которое, надеюсь, поможет другим. Недостаток - это способ уловить ошибку в первом запросе. (Скрытые действия myqsqli_multi_query трудно понять.)

Теперь проверьте записи в массиве $ err.

$q[1] = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20"; 
$q[2] = "SELECT * FROM inventory"; 
$ord = array(); 
$invent = array(); 
$err = array(); 
$c = 1; 

if(mysqli_multi_query($link, implode(';', $q))) { 
    do { 
     // fetch results 
     if($result = mysqli_use_result($link)) 
      while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) { 
       if(count($row) > 17) 
        $orders[] = $row; 
       elseif(count($row) == 6) 
        $inv[] = $row; 
      } 
     } 
     $c++; 
     if(!mysqli_more_results($link)) 
      break; 
     if(!mysqli_next_result($link) || mysqli_errno($link)) { 
      // report error 
      $err[$c] = mysqli_error($link); 
      break; 
     } 
    } while(true); 
    mysqli_free_result($result); 
} 
else 
    $err[$c] = mysqli_error($link); 

mysqli_close($link); 
0

Это работает для двух запросов.

Если ошибка указана в первом, ответ на первый запрос на PHP - это сообщение об ошибке, а для второго (которое не будет срабатывать), сообщение начнется первым.

Если ошибка указана во втором, возвращается первый ответ, а второй - сообщение об ошибке.

Я использую ассоциативные массивы и элементы массива nixing [0]. Это добавляет клавишу ['Error'], только если есть соответствующая ошибка.

Наконец, я не лучший PHPer, поэтому вам решать, что уродливо.

$query_nbr=0; 

if (mysqli_multi_query($link,$query)) 
{ 
    do 
    { 
    unset($field_info) ;   $field_info = array() ; 
    unset($field_names) ;   $field_names = array() ; 
    unset($values) ;    $values = array(array()) ; 

    if ($result = mysqli_store_result($link)) 
    { 
     $query_nbr += 1 ; 
     $rows_found = mysqli_num_rows($result); 
     $fields_returned = mysqli_num_fields($result); 
     $field_info = mysqli_fetch_fields($result); 

     $field_nbr=0; 
     foreach ($field_info as $fieldstuff) 
     { $field_nbr +=1 ; 
     $field_names[$field_nbr] = $fieldstuff->name ; 
     } 

     $now = date("D M j G:i:s T Y") ; 

     if ($query_nbr == 1) 
     { 
     $result_vector1 = array('when'=>$now) ; 
     $result_vector1['nrows']=0; 
     $result_vector1['nrows']=$rows_found ; 
     $result_vector1['nfields']=$fields_returned ; 
     $result_vector1['field_names']=$field_names ; 
     } 
     else 
     { 
     $result_vector2 = array('when2'=>$now) ; 
     $result_vector2['nrows2']=0; 
     $result_vector2['nrows2']=$rows_found ; 
     $result_vector2['nfields2']=$fields_returned ; 
     $result_vector2['field_names2']=$field_names ; 
     } 

     $row_nbr=0 ; 
     while ($row = mysqli_fetch_array($result, MYSQLI_BOTH)) 
     { 
     $row_nbr++ ; 
     for ($field_nbr=1;$field_nbr<=$fields_returned;$field_nbr++) 
     { 
      $values[$row_nbr][$field_names[$field_nbr]] =$row[$field_nbr-1] ; 
     } 
     } 
     mysqli_free_result($result) ; 

     unset($values[0]) ; 
     if ($query_nbr == 1) 
     {$result_vector1['values']=$values ;} 
     else 
     {$result_vector2['values2']=$values ;} 

    } // EO if ($result = mysqli_store_result($link)) 

    } while (mysqli_more_results($link) && mysqli_next_result($link)) ; 

} // EO if (mysqli_multi_query($link,$query)) 
else 
{ 
    // This will be true if the 1st query failed 
    if ($query_nbr == 0) 
    { 
    $result_vector1['Error'] = "MySQL Error #: ".mysqli_errno($link).": ".mysqli_error($link) ; 
    $result_vector2['Error'] = "MySQL Error in first query." ; 
    } 

} // EO MySQL 

// Here we only made it through once, on the 2nd query 
if ($query_nbr == 1 && $nqueries == 2 && empty($result_vector2)) 
{ 
    $result_vector2['Error'] = "MySQL Error #: ".mysqli_errno($link).": ".mysqli_error($link) ; 
} 
Смежные вопросы