避免多次准备相同PDO语句的最佳方法是什么?

时间:2013-09-25 20:14:39

标签: php mysql sql prepared-statement

目前我将准备好的语句保存到私有变量中,因为我忽略了它们在深度方面的实际工作方式,并且只是为了以防万一。

所以问题很简单,如果我迭代相同的$PDO->prepare(),它会再次准备相同的查询吗?

foreach( $arr as $docid ) {
  if( $this->dbLink === null ) { // PDO resource, saved in the object.
     throw new Exception( 'Must first connect to DB' );
  }
  if( $this->queryCheckAccess === null ) {
    $query = 'SELECT * from something where id = :id';
    $this->queryCheckAccess = $this->dbLink->prepare($query);
  }
  else {
    $result = $this->queryCheckAccess->execute(array(':id'=>$docid));
  }
}

重要吗?或者DB Engine / PHP足够聪明,知道它是相同的预处理语句吗?

非常感谢。

-----------------编辑--------------

我想我被误解了。

我问的是如果我这样做会发生什么:

$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);

如果我这样做会发生什么:

if( $this->queryCheckAccess === null ) {
  $query = 'SELECT * from something where id = :id';
  $this->queryCheckAccess = $this->dbLink->prepare($query);
}

引擎会在第一个示例中准备查询4次吗?或者会注意到它是相同的查询,只是"跳跃"是什么?

4 个答案:

答案 0 :(得分:2)

您的代码只准备一次查询,因为在第一次循环迭代之后,它不是NULL,因此条件块将不会运行。但每次循环检查条件都是浪费时间。

但是要回答你的问题,如果你准备()相同的查询,它确实做了多余的工作,即使查询与你之前准备的查询相同。所以你应该避免这种情况。

但你根本不需要在循环内准备。在开始循环之前准备一次,并将变量绑定到参数。您不需要在循环中每次绑定,只需更改该变量的值。

if( $this->dbLink === null ) { // PDO resource, saved in the object.
    throw new Exception( 'Must first connect to DB' );
}
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$this->queryCheckAccess->bindParam(':id' => $docidparam);
foreach( $arr as $docid ) {
    $docidparam = $docid;
    $result = $this->queryCheckAccess->execute();
}

我不确定您是否可以绑定变量并将其用作循环变量,可能存在范围冲突。

此查询的另一个建议是:为什么不运行一个查询来搜索值列表?

$list = implode(",", array_fill(1, count($arr), "?"));
$query = "SELECT * FROM something WHERE id IN ( $list )";
$this->queryCheckAccess = $this->dbLink->prepare($query);
$this->queryCheckAccess->execute($arr);

PS:你也应该检查错误。如果启用PDO错误模式EXCEPTION,则错误将自动引发异常。如果未启用该模式,则需要检查prepare()和execute()的返回值,如果出现错误,则返回 false

答案 1 :(得分:0)

我只是运行类似于您的示例的代码,并启用MySQL Query LOG我发现所有准备请求都发送到MySQL服务器

   Prepare  SELECT * FROM test_table WHERE username = ?
   Close stmt   
   Prepare  SELECT * FROM test_table WHERE username = ?
   Close stmt   
   Prepare  SELECT * FROM test_table WHERE username = ?
   Close stmt   
   Prepare  SELECT * FROM test_table WHERE username = ?
   Close stmt

测试代码:

$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth->bindParam(1, $user);
$sth->execute();

然后,最好的方法是准备一次,并绑定不同的值,然后执行。

$sth = $dbh->prepare($sql);
$user = "test";
$sth->bindParam(1, $user);
$sth->execute();

$user = "test2";
$sth->bindParam(1, $user);
$sth->execute();

$user = "test";
$sth->bindParam(1, $user);
$sth->execute();

答案 2 :(得分:-1)

不,这是准备好的陈述的主要特征之一。如果您要多次运行相同的查询但使用不同的变量,那么准备查询将为您提高速度。特别是如果你使用交易(需要InnoDB存储引擎)。

答案 3 :(得分:-1)

  • 要回答标题中的问题(与正文中的问题完全不同),避免多次准备相同语句的最佳方法,显然是避免运行多个类似的查询

  • 要回答问题正文中的问题 - 不,数据库引擎/ PHP不够“智能”,足以知道它是同一个查询再次准备好了。每次新的prepare()调用都会创建另一个语句。我会首先讨厌这种“聪明”的行为。你的工具“更聪明”,你得到的结果越不可预测。

  • 要回答代码中的真正的问题,智能开发人员会使用正确的工具为自己省去麻烦。
    使用safeMysql整个混乱将减少到一个查询和一行代码

    $data = $this->dbLink->getAll('SELECT * from somth where id IN (?a)', $arr);
    

    S0 - 没有多重查询,没有多重准备,没有多重问题。

顺便说一下,你的代码丢失了第一个id。 然而,如果您不使用结果,那么您将失去所有这些但是最后一个。