【问题标题】:Recursion in prepared statements准备好的语句中的递归
【发布时间】:2011-02-12 10:38:59
【问题描述】:

我一直在使用 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 循环中对 fetch 的调用只会工作一次,然后函数再次调用自己,结果集也改变了。

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

一种方法是使用 fetchAll(),但它在手册中说有很大的开销。这样做的重点是提高效率。

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

或者有什么方法可以改写吗?

【问题讨论】:

  • 我只是做了一些(不是超级科学的)测试我非常相似的递归函数代码并得出结论,就我的目的而言,准备好的语句总体上较慢。如果您必须在每次使用前重新准备一份声明,那么准备一份声明的意义何在?我一定是错过了什么。

标签: php mysql recursion pdo prepared-statement


【解决方案1】:

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

事实上,PDO 会在您发出新的 prepare 时自动执行。

当你递归调用函数时:

  • 初始句柄分配(1)
  • 获取(1)的第一条记录
  • 递归调用函数。 (1) 的值驻留在递归堆栈中。
  • 分配了新句柄(2)无效(1)
  • (2)获取第一条记录
  • 函数返回
  • 您尝试获取(1) 的下一条记录并失败,因为它无效

因此,MySQL 不支持递归,这意味着您必须在PHP 一侧使用fetchAll

【讨论】:

  • 你指的是什么句柄?您可以在单个会话中拥有许多预准备语句句柄(我相信每个会话限制为大约 500 个服务器端预准备语句,并且总共有 `max_prepared_stmt_count` 语句,但这不适用于使用客户端库创建的预准备语句)。结果无法自行处理。
【解决方案2】:

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

根据语句是什么,您可能会重写它以一次返回所有结果,然后构建树。

【讨论】:

  • @outis: $handle 是静态的,原因是:会话中只能有一个活动语句。
  • @Quassnoi:我意识到$handle 是静态的是有原因的。但是,静态变量根本不会像 Rob 在递归调用中希望的那样工作。
  • @Quassnoi:你所说的“活跃”是什么意思?一个会话中可以有许多准备好的语句,尽管每个语句只包含一个结果集。
【解决方案3】:

如果您使用相同的变量,(因为 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];

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 2016-03-23
    • 2015-06-27
    相关资源
    最近更新 更多