PDO的查询与执行


129

他们俩都做同一件事,只是做事不同吗?

除了使用prepare之间还有什么区别

$sth = $db->query("SELECT * FROM table");
$result = $sth->fetchAll();

$sth = $db->prepare("SELECT * FROM table");
$sth->execute();
$result = $sth->fetchAll();

Answers:


145

query 运行标准SQL语句,并要求您正确转义所有数据,以避免SQL注入和其他问题。

execute运行一个准备好的语句,该语句使您可以绑定参数以避免转义或引用参数。execute如果您多次重复查询,效果也会更好。准备的语句示例:

$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories);
$sth->bindParam(':colour', $colour);
$sth->execute();
// $calories or $color do not need to be escaped or quoted since the
//    data is separated from the query

最佳实践是坚持准备好的语句并execute提高安全性

另请参阅:PDO准备好的语句是否足以防止SQL注入?


该链接指向的问题答案很愚蠢,已在评论中受到批评。
常识

因此,如果您在上使用“准备” : calories ,那么相当于mysql_real_escape_string()停止注射还是您需要做更多的事情$sth->bindParam(':calories', $calories);来增强安全性?
2012年

为什么query返回PDOStatement而不是bool之类的execute
狮子座


47

不,他们不一样。除了在客户端提供转义功能外,准备好的语句在服务器端还会被编译一次,然后可以在每次执行时传递不同的参数。这意味着您可以:

$sth = $db->prepare("SELECT * FROM table WHERE foo = ?");
$sth->execute(array(1));
$results = $sth->fetchAll(PDO::FETCH_ASSOC);

$sth->execute(array(2));
$results = $sth->fetchAll(PDO::FETCH_ASSOC);

尽管在小范围内并不引人注目,但它们通常可以使您提高性能。阅读更多有关准备好的语句(MySQL版本)的信息


我喜欢您解释为什么会更快的方式。
timfreilly 2012年


3

Gilean的回答很好,但我只是想补充一点,有时最佳实践很少有例外,您可能想两种方法都测试一下您的环境,以了解最有效的方法。

在一个案例中,我发现它可以query更快地实现我的目的,因为我是从运行PHP7的Ubuntu Linux盒中,通过对MS SQL Server的Microsoft ODBC驱动程序支持不佳,批量传输受信任的数据。

我遇到了这个问题,因为我的ETL脚本运行时间很长,因此我试图提高速度。在我看来,这query可能比prepare&更快,execute因为它只调用一个函数而不是两个。参数绑定操作提供了出色的保护,但是它可能很昂贵,并且在不必要的情况下可以避免。

考虑到几个罕见的情况

  1. 如果由于Microsoft ODBC驱动程序不支持已准备好的语句而无法重用

  2. 如果您不担心清理输入,可以进行简单的转义。可能是因为Microsoft ODBC驱动程序不支持绑定某些数据类型

  3. PDO::lastInsertId 不被Microsoft ODBC驱动程序支持。

这是我用来测试环境的一种方法,希望您可以复制它或在您的环境中更好的东西:

首先,我在Microsoft SQL Server中创建了一个基本表

CREATE TABLE performancetest (
    sid INT IDENTITY PRIMARY KEY,
    id INT,
    val VARCHAR(100)
);

现在是性能指标的基本定时测试。

$logs = [];

$test = function (String $type, Int $count = 3000) use ($pdo, &$logs) {
    $start = microtime(true);
    $i = 0;
    while ($i < $count) {
        $sql = "INSERT INTO performancetest (id, val) OUTPUT INSERTED.sid VALUES ($i,'value $i')";
        if ($type === 'query') {
            $smt = $pdo->query($sql);
        } else {
            $smt = $pdo->prepare($sql);
            $smt ->execute();
        }
        $sid = $smt->fetch(PDO::FETCH_ASSOC)['sid'];
        $i++;
    }
    $total = (microtime(true) - $start);
    $logs[$type] []= $total;
    echo "$total $type\n";
};

$trials = 15;
$i = 0;
while ($i < $trials) {
    if (random_int(0,1) === 0) {
        $test('query');
    } else {
        $test('prepare');
    }
    $i++;
}

foreach ($logs as $type => $log) {
    $total = 0;
    foreach ($log as $record) {
        $total += $record;
    }
    $count = count($log);
    echo "($count) $type Average: ".$total/$count.PHP_EOL;
}

我在自己的特定环境中进行了多次不同的试验和计数,结果始终queryprepare/ 快20-30%execute

5.8128969669342准备
5.8688418865204准备
4.2948560714722查询
4.9533629417419查询
5.9051351547241准备
4.332102060318查询
5.9672858715057准备
5.0667371749878查询
3.8260300159454查询
4.0791549682617查询
4.3775160312653查询
3.6910600662231查询
5.2708210945129准备
6.2671611309052准备
6.0
(7.3791449069977)准备(7.3791449069769)
(平均)7.792449069977 (准备平均)(7.9791449069977)(平均)7.791449069977 (准备)7.3791449069977 (平均)7.691449069977 (准备)7.3791449069977 (平均)7.691449069977 (准备)7.3791449069977 (平均)

我很好奇该测试在其他环境(例如MySQL)中如何进行比较。


“经验证据”(或更确切地说是人工测试)的问题在于它们反映了您(未知)的特定条件,并且可能对其他人有所不同,更不用说现实世界中的经验证据了。然而,有些人会认为这是理所当然的,并进一步传播了这个词。
您的常识

@YourCommonSense我完全同意,并感谢您的反馈,因为从我的大胆罕见情况来看,我认为这很明显。我猜想是健康的,但忘了它并不明显。请查看我修改后的答案,并让我知道如何对其进行改进。
杰夫·普基特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.