2

使用來自其他地方的示例來更好地捕捉「隱藏」錯誤。儘管下面的代碼會捕獲並返回一個錯誤,但是是否有可能改進這一點以報告發生錯誤的查詢?如何使用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位置= 'IN' ORDER BY ORDERNUM DESC LIMIT 20,語法錯誤=表 '[someDB] .ordersXYZ' 不存在

錯誤在第二個查詢: 如果你的第一個查詢是成功的,但你的第二個查詢試圖訪問像一個不存在的表:inventory2
$rows["Orders"]將持有該行的數據,將是var_export()「版,$row["Inventory"]將不存在,你會看到這樣的響應:

查詢鍵=庫存,查詢= SELECT * FROM inventory2,語法錯誤=表 '[someDB] .inventory2' 不存在

沒有錯誤: 如果這兩個查詢是免費的錯誤,你$rows陣列將充滿期望的數據和var_export()「版,並不會有錯誤響應。查詢的數據保存在$rows中,您可以從$rows["Orders"]$rows["Inventory"]訪問您想要的內容。


注意事項:

  1. 您可能注意到,我在同一時間,使變量聲明和有條件的檢查,這使得代碼更幹。

  2. 由於我的方法在elseif行上使用了帶分號的implode(),請確保不要在查詢中添加尾隨分號。

  3. 這組查詢總是返回設置,因爲所有人都SELECT查詢,如果你有疑問的是affect_rows,你會發現這個鏈接(https://stackoverflow.com/a/22469722/2943403)一些有用的信息的混合組合的結果。

  4. mysqli_multi_query()將在出現錯誤時立即停止運行查詢。如果您希望抓住「所有」錯誤,您會發現永遠不會有多於一個。

  5. 寫入OP的問題和解決方案中的條件性斷點是不可取的。雖然在其他情況下可以正確使用自定義斷點,但在這種情況下,斷點應位於do()塊的while()語句中。

  6. 返回零行不會導致錯誤信息的查詢 - 它只是不會在$rows創建任何子陣,因爲while()循環將不進入。

  7. 通過使用key()函數,可以避免對每個結果集行中的列進行計數的OP的if/elseif條件。這是更好的做法,因爲在某些情況下在每次迭代中運行條件可能會變得很昂貴。請注意,在迭代的每個do()結束時,數組指針在$q內部被提前。這是一個額外的技術,你不會在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 statementspdo爲你的數據庫交互的安全性更高的水平。

0

在你做循環,添加一個計數器,每一個成功的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的響應是錯誤消息,和用於第二(這將不會觸發),消息在第一閒聊。

如果錯誤是在第二,第一的響應返回和第二得到錯誤信息。

我使用關聯數組和坭興數組元素[0]。 僅當存在相關錯誤時纔會添加['錯誤']鍵。

最後,我不是最好的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) ; 
} 
相關問題