一次使用多个PDO准备好的语句

时间:2020-02-08 18:48:24

标签: php mysql pdo

这是一个基于6年前在PDO :: prepare的PHP.net手册中很容易错过的非常注释的确认/澄清请求,我在其他地方没有看到过讨论(即使在伟大的phpdelusions博客中)。如果确实我认为应该对它进行更广泛的覆盖,并为其他人突出显示搜索(或者如果没有的话,则需要进行隐藏),这样的可能性很大。

这是评论(Hayley Watson):

可以针对单个连接预先准备多个语句。 只要该连接保持打开状态,就可以执行语句并从中获取 您可以按照任意顺序进行多次操作;他们的“准备-执行-获取”步骤可以交错 以哪种方式最好。

因此,如果您可能经常使用多个语句(也许在一个循环中 交易),您可能需要考虑准备要使用的所有语句。

我有一些必须像(伪代码)一样运行的代码:

foreach (fetchAll row with PDO) {
    process row results
    if (condition)
        update table with processed results
    else
        delete row no longer needed
}

根据该评论,我可以在循环之前创建两个准备好的语句,一个用于更新查询,一个用于删除查询,然后(仅)在循环内执行。只要句柄是不同的并保留下来,连接就应该同时缓存它们,我可以互换使用它们,而不必在循环内解析任何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起)?如果是这样,我猜可以在解决方案中共享用于这种形式的代码的其他简洁应用程序。

2 个答案:

答案 0 :(得分:4)

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

您可以将以下代码与缠结的语句一起运行,它将起作用。

$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问题时,这可以为您带来一些性能优势。您准备一次内部查询,然后在循环内多次执行它。

但是,如果您想运行unbuffered query(这很少使用),那么结果查询可能会出现问题。默认情况下,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());

它将产生:

致命错误:未捕获的PDOException:SQLSTATE [HY000]:常规错误:2014当其他未缓冲的查询处于活动状态时,无法执行查询。考虑使用PDOStatement :: fetchAll()。另外,如果您的代码只打算针对mysql运行,则可以通过设置PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY属性来启用查询缓冲。

答案 1 :(得分:1)

好问题!第一句话?

    $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秒。

在底部运行单独的语句,同时更新和删除都比其他语句快得多。

是因为if语句吗?

我正在运行php 7.4.0和mysql 8.0

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