Php 同时使用多个PDO准备语句

Php 同时使用多个PDO准备语句,php,mysql,pdo,Php,Mysql,Pdo,这是一个确认/澄清请求,基于6年前在PHP.net PDO::prepare手册中的一条很容易遗漏的评论,我在其他地方(甚至在伟大的phpdelusions博客中)都没有讨论过。如果这是真的,我觉得它应该得到更广泛的报道,并为其他人突出搜索(或需要揭穿,如果不是),那么这是一个非常强大的可能性 以下是海莉·沃森的评论: 可以针对单个连接预先准备几个语句。 只要该连接保持打开状态,就可以执行语句并从中获取语句 无论你喜欢什么顺序,随时都可以;它们的“prepare-execute-fetch”步骤

这是一个确认/澄清请求,基于6年前在PHP.net PDO::prepare手册中的一条很容易遗漏的评论,我在其他地方(甚至在伟大的phpdelusions博客中)都没有讨论过。如果这是真的,我觉得它应该得到更广泛的报道,并为其他人突出搜索(或需要揭穿,如果不是),那么这是一个非常强大的可能性

以下是海莉·沃森的评论:

可以针对单个连接预先准备几个语句。 只要该连接保持打开状态,就可以执行语句并从中获取语句 无论你喜欢什么顺序,随时都可以;它们的“prepare-execute-fetch”步骤可以交错进行 无论哪种方式最好

因此,如果您可能经常使用多个语句(可能在 交易),您可能会考虑准备所有您前面使用的语句。 我有一些代码必须像(伪代码)一样运行:

根据该注释,我可以在循环之前创建两个准备好的语句,一个用于update查询,一个用于delete查询,然后(仅)在循环中执行。只要句柄是不同的并被保留,连接就应该缓存这两个句柄,我可以互换使用它们,并且我不必在循环中进行任何SQL语句解析,这将非常低效:

// do statement prepare/execute/fetchAll for main loop, then...
$update_stmt = $PDO->prepare($update_query);
$delete_stmt = $PDO->prepare($delete_query);
foreach (fetchAll row) {
    process row results
    if (condition)
        $update_stmt->execute(some_processed_values);
    else
        $delete_stmt->execute(some_other_values);
}

由于这里的大多数问题一次只讨论使用一个准备好的语句,并且如果广泛应用,这对代码效率有很好的影响,是否有人想确认这确实是事实(至少从PHP7开始)?如果是这样的话,我想这种代码形式的其他整洁应用程序可以在解决方案中共享。

问得好!第一句话

    $id = '2';
    $username = 'John';
    if ($id === 2){
        $array = array(':username'=> $username, ':id'=>$id);
        $sql = "UPDATE users SET username = ? WHERE id = ?";
    }else{
        $array = array(':id'=>$id);
        $sql = "DELETE FROM users WHERE id = ?";
    }

$stmt = $pdo->prepare($sql);
$stmt->execute($array);
第二项声明:

$sql = "UPDATE users SET username = ? WHERE id = ?";
$sql = "DELETE FROM users WHERE id = ?";
$stmt1 = $pdo->prepare($sql);
$stmt2 = $pdo->prepare($sql);

    if ($id === 2){
        $array = array(':username'=> $username, ':id'=>$id);
        $stmt1->execute($array);
    }else{
        $array = array(':id'=>$id);
        $stmt2->execute($array);
    }
简单陈述:

$stmt = $pdo->prepare("UPDATE users SET username = ? WHERE id = ?")->execute([':username'=> $username, ':id'=>$id]);
$stmt = null;

$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([':id'=>$id]);
$stmt = null;
第一条语句的运行速度比第二条语句快1秒

在底部运行separate语句时,同时更新和删除语句的速度要比其他语句快得多

这是因为if语句吗

我正在运行PHP7.4.0和mysql 8.0


如果有人想尝试,则更新。

同时使用多个准备好的语句并按顺序执行它们没有问题

您可以使用交织的语句运行如下代码,这样就可以了

$stmt1 = $pdo->prepare('INSERT INTO addplate(Plate) VALUES(?)');
$stmt2 = $pdo->prepare('UPDATE addplate SET Plate=? WHERE Plate=?');

$stmt1->execute(['val1']);
$stmt2->execute(['val2', 'val1']);
$stmt1->execute(['val1']);
$stmt2->execute(['val2', 'val1']);
$stmt1->execute(['val1']);
当由于某种原因无法避免N+1问题时,这可以为您带来一些性能优势。准备一次内部查询,然后在循环内执行多次

但是,如果您想运行结果生成查询,这可能是一个问题(很少使用)。PDO在默认情况下执行缓冲查询,因此您需要关闭它们以运行此问题

$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

$stmt1 = $pdo->prepare('SELECT * FROM addplate WHERE Plate=?');
$stmt2 = $pdo->prepare('SELECT * FROM addplate WHERE Plate=?');

$stmt1->execute(['val1']);
var_dump($stmt1->fetch());
$stmt2->execute(['val2']); // <-- Error if stmt1 still has more records
var_dump($stmt2->fetch());
$pdo->setAttribute(pdo::MYSQL\u ATTR\u USE\u BUFFERED\u QUERY,false);
$stmt1=$pdo->prepare('SELECT*FROM addplate,其中Plate=?');
$stmt2=$pdo->prepare('SELECT*FROM addplate,其中Plate=?');
$stmt1->execute(['val1']);
变量转储($stmt1->fetch());
$stmt2->execute(['val2']);//fetch());
它将产生:

致命错误:未捕获PDOException:SQLSTATE[HY000]:一般错误:2014无法在其他未缓冲查询处于活动状态时执行查询。考虑使用pdoCalp::FutAccess()。或者,如果您的代码只在mysql上运行,您可以通过设置PDO::mysql_ATTR_USE_BUFFERED_query属性来启用查询缓冲


是的,在循环外部使用预先准备好的语句将给您带来性能上的好处,因为MySql将缓存它。我可以确认这就是如何使用预先准备好的语句。我看不出有什么问题。php.net或任何其他文档中都没有提到任何限制,这只是使用预处理语句的诸多好处之一。您只是尝试过检查一下吗?如果您可以检查性能,那就太好了。我看不出有太大的差异,即使预处理语句比其他语句更快..投票赞成,但我要补充的是,无缓冲查询只对
SELECT
或其他返回结果集的语句(例如,
SHOW
CALL
description
)重要。像
UPDATE
DELETE
这样的语句没有结果集,因此没有缓冲区。感谢您的帮助,特别是关于设置缓冲区与非缓冲区属性的评论。很明显,缓冲对于在循环中预先准备多个语句是必不可少的。但我看到了一些相互矛盾的评论,关于该属性设置是在运行时(每个PDO查询)实际工作,还是仅在连接创建时有效。我的代码最好同时使用这两种方法,因为unbuffered可以更好地处理一些fetch循环。对于较新的PHP7/MySQL是否有明确的答案?忘记无缓冲查询吧。你不需要它们。他们的麻烦比他们值得的多。
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

$stmt1 = $pdo->prepare('SELECT * FROM addplate WHERE Plate=?');
$stmt2 = $pdo->prepare('SELECT * FROM addplate WHERE Plate=?');

$stmt1->execute(['val1']);
var_dump($stmt1->fetch());
$stmt2->execute(['val2']); // <-- Error if stmt1 still has more records
var_dump($stmt2->fetch());