使用SO上其他地方的示例来更好地捕捉“隐藏”错误。虽然下面的代码会捕获并返回错误,但是是否可以改进此报告以报告错误发生的查询?
使用下面的代码,输出为:
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);
}
答案 0 :(得分: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 location =&#39; IN&#39; ORDER BY orderNum DESC LIMIT 20,语法错误=表&#39; [someDB] .ordersXYZ&#39;不存在
第二次查询时出错:
如果您的第一个查询成功,但您的第二个查询尝试访问不存在的表,例如:inventory2
$rows["Orders"]
将保留所需的行数据,var_export()
,$row["Inventory"]
将不存在,您将看到此响应:
查询键=库存,查询= SELECT * FROM inventory2,语法错误=表&#39; [someDB] .inventory2&#39;不存在
没有错误:
如果两个查询都没有错误,那么您的$rows
数组将填充所需数据并var_export()
,并且不会出现错误响应。通过$rows
保存查询的数据,您可以从$rows["Orders"]
和$rows["Inventory"]
访问您想要的内容。
注意事项:
您可能会注意到我正在同时进行变量声明和条件检查,这会使代码更加干燥。
由于我的方法在implode()
行上使用elseif
分号,请确保不要在查询中添加尾部分号。
这组查询始终返回结果集,因为所有查询都是SELECT查询,如果您有affect_rows
的混合查询集合,您可以在此链接中找到一些有用的信息({{3} })。
mysqli_multi_query()
将停止运行查询。如果你期望赶上&#34;所有&#34;错误,你会发现永远不会有多个错误。
不建议在OP的问题和解决方案中编写条件断点。虽然自定义断点可能在其他情况下正确使用,但对于这种情况,断点应位于while()
块的do()
语句内。
返回零行的查询不会导致错误消息 - 它只是不会在$rows
中创建任何子数组,因为不会输入while()
循环。 / p>
通过使用key()
函数,可以避免OP计算每个结果集行中列的if/elseif
条件。这是更好的做法,因为在某些情况下,在每次迭代中运行条件都会变得昂贵。请注意,在每个$q
迭代结束时,数组指针在do()
内部进行了推进。这是一个你不会在php手册页上找到的额外技术;它允许key()
按预期工作。
当然,<div><pre>var_export()...</pre></div>
行可以从您的工作代码中删除 - 这纯粹是为了演示。
如果您要在此代码块之后再次运行重用变量,请务必清除所有使用的变量,以便残留数据不会干扰。例如$mysqli_error=null; // clear errors
&amp; reset($q); // reset array pointer
。
谨慎地注意这个有点模糊的警告:https://stackoverflow.com/a/22469722/2943403:
如果有很多处理,就不应该使用mysqli_use_result() 客户端执行,因为这将占用服务器和 防止其他线程更新数据所在的任何表 被抓住了。
最后,最重要出于安全原因,请勿公开显示查询或查询错误信息 - 您不希望险恶的人看到此类反馈。同样重要的是,始终保护您的查询免受注入黑客攻击。如果您的查询包含用户提供的数据,则需要在mysqli_multi_query()
中使用数据之前将数据过滤/清理为死亡。事实上,在处理用户输入时,我强烈建议远离mysqli_multi_query()
并使用http://php.net/manual/en/mysqli.use-result.php或prepared statements进行数据库交互,以获得更高级别的安全性。
答案 1 :(得分:0)
在你的do循环中,添加一个计数器,每个成功的mysqli_next_result都会递增计数器。一旦mysqli_next_result返回false,也输出计数器。
答案 2 :(得分: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);
答案 3 :(得分:0)
这适用于两个查询。
如果错误在第一个中,则第一个查询到PHP的响应是错误消息,而对于第二个(不会触发),第一个查询的消息就会响起。
如果错误在第二个中,则返回第一个响应,第二个响应获取错误消息。
我正在使用关联数组和nixing数组元素[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) ;
}