准备好的语句中的递归
我一直在使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您不能嵌套语句句柄:您需要先关闭先前打开的句柄,然后再在单个会话中打开另一个句柄。
事实上,当您发出新的准备时,
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:
(1)
(1)
(1)
resides in the recursion stack.(2)
, invalidating(1)
(2)
(1)
and fail since it's invalidThus said,
MySQL
does not support recursion on its side and that means you'll have to do it onPHP
side, usingfetchAll
.真正的问题是
$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.
如果您使用相同的变量,(因为 pdo bindValue)每次值都与第一个相同。所以这会失败:
结果:
所以你想做丑陋的把戏,然后:
结果:
If you use same variables, (because of the pdo bindValue) every time value is the same with the first one. So this will FAIL:
result:
So you want to do ugly trick, then:
result: