准备好的语句中的递归

发布于 2024-08-29 22:55:18 字数 883 浏览 6 评论 0原文

我一直在使用 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 驱动程序内部无论如何都会使用准备好的语句,因此在每个递归调用上创建新句柄的开销很小。我个人认为这违背了要点。

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

I've been using PDO and preparing all my statements primarily for security reasons. However, I have a part of my code that does execute the same statement many times with different parameters, and I thought this would be where the prepared statements really shine. But they actually break the code...

The basic logic of the code is this.

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;
}

It looked fine to me, but it was missing out a lot of rows. Eventually I realised that the statement handle was being changed (executed with different param), which means the call to fetch in the while loop will only ever work once, then the function calls itself again, and the result set is changed.

So I am wondering what's the best way of using PDO prepared statements in a recursive way.

One way could be to use fetchAll(), but it says in the manual that has a substantial overhead. The whole point of this is to make it more efficient.

The other thing I could do is not reuse a static handle, and instead make a new one every time. I believe that since the query string is the same, internally the MySQL driver will be using a prepared statement anyway, so there is just the small overhead of creating a new handle on each recursive call. Personally I think that defeats the point.

Or is there some way of rewriting this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

请恋爱 2024-09-05 22:55:18

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

事实上,当您发出新的准备时,PDO 会自动执行此操作。

当递归调用该函数时:

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

因此说,MySQL 不支持递归,这意味着您将必须在 PHP 端使用 fetchAll 执行此操作。

You cannot nest the statement handles: you need to close the previously open handle before opening another one within a single session.

In fact, PDO does if automatically when you issue a new prepare.

When you call the function recursively:

  • The initial handle is allocated (1)
  • The first record is fetched of (1)
  • The function is called recursively. Value of (1) resides in the recursion stack.
  • The new handle is allocated (2), invalidating (1)
  • The first record is fetched of (2)
  • The function returns
  • You try to fetch the next record of (1) and fail since it's invalid

Thus said, MySQL does not support recursion on its side and that means you'll have to do it on PHP side, using fetchAll.

以可爱出名 2024-09-05 22:55:18

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

根据语句的内容,您可能会重写它以立即返回所有结果,然后构建树。

The real problem is that $handle is static. Static variables are problematic for recursion when state needs to be preserved across a recursive call, not just for prepared statements. In this case, the recursive call executes a new query, discarding the previous state. PDO::fetchAll is indeed the only option if you want a single prepared query.

Depending on what the statement is, you could potentially rewrite it to return all results at once, building the tree after that.

眼波传意 2024-09-05 22:55:18

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

If you use same variables, (because of the pdo bindValue) every time value is the same with the first one. So this will FAIL:

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

result:

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

So you want to do ugly trick, then:

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

result:

$key[0] = $value[0];
$key[1] = $value[1];
$key[2] = $value[2];
$key[3] = $value[3];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文