2010-04-26 59 views
7

我一直在使用PDO并且主要是为了安全原因准备我的所有陈述。然而,我有一部分代码确实多次用不同的参数执行相同的语句,我认为这将是准备好的语句真正发光的地方。但他们实际上破坏了代码...准备好的陈述中的递归

代码的基本逻辑是这样的。

function someFunction($something) { 
    global $pdo; 

    $array = array(); 

    static $handle = null; 
    if (!$handle) { 
    $handle = $pdo->prepare("A STATEMENT WITH :a_param"); 
    } 

    $handle->bindValue(":a_param", $something); 
    if ($handle->execute()) { 
    while ($row = $handle->fetch()) { 
     $array[] = someFunction($row['blah']); 
    } 
    } 

    return $array; 
} 

它看起来很好,但它错过了很多行。最终,我意识到语句句柄正在被改变(用不同的参数执行),这意味着在while循环中获取的调用只能工作一次,然后函数再次调用自己,并且结果集被改变。

所以我想知道什么是以递归方式使用PDO准备语句的最佳方式。

一种方法可能是使用fetchAll(),但它在手册中说有很大的开销。这整个意义在于让它更有效率。

我可以做的另一件事是不重用一个静态句柄,而是每次都做一个新的。我相信由于查询字符串是相同的,因此MySQL内部的驱动程序无论如何都会使用准备好的语句,所以在每次递归调用时创建一个新句柄的开销很小。就我个人而言,我认为这打破了这一点。

或者有什么方法可以重写?

+0

我只是做了一些(不是超级科学)测试我非常相似的递归函数代码,并有浓对于我的目的而言,准备好的陈述总体上较慢。如果您在每次使用前必须重新准备,那么准备一份声明有什么意义?我肯定错过了什么。 – 2012-11-14 19:32:13

回答

2

不能嵌套语句句柄:在单个会话中打开另一个句柄之前,需要先关闭先前打开的句柄。

实际上,PDO会在您发出新准备时自动执行。

当你调用的函数递归:

  • 最初的手柄被分配(1)
  • 第一个记录是牵强的(1)
  • 函数被递归调用。 (1)的值驻留在递归堆栈中。
  • 新的手柄被分配(2)无效(1)
  • 第一个记录是牵强的(2)
  • 该函数返回
  • 你尝试获取的(1)下一个记录和失败,因为它是无效的

因此说,MySQL不支持它的递归,这意味着你必须在PHP一边,使用fetchAll

+0

你指的是什么手柄?你可以在一个会话中有很多准备好的语句句柄(我认为每个会话有大约500个服务器端准备语句的限制,总共有max_prepared_stmt_count'语句,但这不适用于使用客户端库创建的预准备语句)。结果没有得到他们自己的处理。 – outis 2010-04-28 00:03:57

0

真正的问题是$handle是静态的。当状态需要在递归调用中保留时,静态变量对于递归是有问题的,而不仅仅是预处理语句。在这种情况下,递归调用执行一个新的查询,放弃以前的状态。如果你想要一个准备好的查询,PDO::fetchAll确实是唯一的选择。

根据声明的内容,您可能会重写它以一次返回所有结果,然后再构建树。

+0

@outis:'$ handle'是静态的,原因是:会话中只能有一个活动语句。 – Quassnoi 2010-04-27 08:55:32

+0

@Quassnoi:我意识到'$ handle'是静态的原因。但是,静态变量根本不会像Rob希望他们在递归调用中那样工作。 – outis 2010-04-27 23:45:58

+0

@Quassnoi:以及“主动”是什么意思?在单个会话中可以有许多预处理语句,但每个语句只能保存一个结果集。 – outis 2010-04-28 00:12:02

0

如果您使用相同的变量(由于pdo bindValue),每次的值与第一个值相同。因此,这将失败:

foreach ($bind_params as $key => $value) { 
    $stmt->bindParam(":$key", $value); 
} 

结果:

$key[0] = $value[0]; 
$key[1] = $value[0]; 
$key[2] = $value[0]; 
$key[3] = $value[0]; 

所以,你想要做的丑陋伎俩,那么:

 $i = 0; 
     foreach ($bind_params as $key => $value) { 
      $i++; 
      $$i = $value; 
      $stmt->bindParam(":$key", $$i); 
     } 

结果:

$key[0] = $value[0]; 
$key[1] = $value[1]; 
$key[2] = $value[2]; 
$key[3] = $value[3];