2

使用 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);
}
4

4 回答 4

7

这是一种方法,不仅可以提高错误消息的质量,还可以改进处理结果集的方式。

$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>" . var_export($rows[$q_key], true) . "</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='IN' ORDER BY orderNum DESC LIMIT 20,语法错误 = 表 '[someDB].ordersXYZ' 不存在

第二个查询错误:如果您的第一个查询成功,但您的第二个查询尝试访问不存在的表,例如:inventory2
$rows["Orders"]将保存所需的行数据并将被var_export()'ed,$row["Inventory"]将不存在,您将看到以下响应:

查询键 = Inventory,查询 = SELECT * FROM inventory2,语法错误 = 表 '[someDB].inventory2' 不存在

没有错误:如果两个查询都没有错误,您的$rows数组将填充所需的数据并var_export()'ed,并且不会有错误响应。将查询的数据保存在 中,您可以从和$rows中访问您想要的内容。$rows["Orders"]$rows["Inventory"]


注意事项:

  1. 您可能会注意到我同时进行变量声明和条件检查,这使代码更加简洁,但一些开发人员更愿意避免这种情况。

  2. 由于我的方法在行implode()上使用分号,因此请elseif确保不要在查询中添加尾随分号。

  3. 这组查询总是返回一个结果集,因为所有查询都是 SELECT 查询,如果您有混合的查询集合,您可以在此链接 ( https://stackoverflow.com/a/22469722/2943403 )affect_rows找到一些有用的信息。

  4. mysqli_multi_query()一旦出现错误,将立即停止运行查询。如果您期望捕获“所有”错误,您会发现永远不会超过一个。

  5. 不建议像在 OP 的问题和解决方案中那样编写条件断点。虽然在其他情况下可能会正确使用自定义断点,但在这种情况下,断点应位于块的while()语句内部。do()

  6. 返回零行的查询不会导致错误消息——它只是不会在其中创建任何子数组,$rows因为while()不会进入循环。

  7. 通过使用该函数,可以避免计算每个结果集行中的列key()的 OP条件。if/elseif这是更好的做法,因为在某些情况下,在每次迭代中运行条件可能会变得很昂贵。请注意,数组指针在每次迭代$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(),因为这将占用服务器并阻止其他线程更新从中获取数据的任何表。

  1. 最后也是最重要的,出于安全原因,不要公开显示查询或查询错误信息——你不希望险恶的人看到这种反馈。同样重要的是,始终保护您的查询免受注入黑客攻击。如果您的查询包括用户提供的数据,您需要在mysqli_multi_query(). 事实上,在处理用户输入时,我非常强烈的建议是远离mysqli_multi_query()并使用mysqlipdo准备好的语句来进行数据库交互,以获得更高级别的安全性。
于 2014-03-18T04:33:14.073 回答
1

为了回答我自己的问题,并且由于文档很差,这里有一个解决方案,希望能对其他人有所帮助。缺少的是一种在第一次查询中捕获错误的方法。(mysqli_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);
于 2012-09-02T16:35:43.037 回答
0

在你的 do 循环中,添加一个计数器,每个成功的 mysqli_next_result 都会增加计数器。一旦 mysqli_next_result 返回 false,也输出计数器。

于 2012-09-02T05:02:45.200 回答
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) ;
}
于 2016-04-30T01:42:37.937 回答