此函数不能与任何返回结果的查询一起使用。这包括 SELECT、OPTIMIZE TABLE 等。
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)
PDO::exec — 执行 SQL 语句并返回受影响的行数
PDO::exec() 在一个函数调用中执行 SQL 语句,返回受语句影响的行数。
PDO::exec() 不会返回 SELECT 语句的结果。对于在程序中只需执行一次的 SELECT 语句,可以考虑使用 PDO::query()。对于需要多次执行的语句,可以使用 PDO::prepare() 准备一个 PDOStatement 对象,并使用 PDOStatement::execute() 执行语句。
PDO::exec() 返回由您发出的 SQL 语句修改或删除的行数。如果未影响任何行,PDO::exec() 返回 0
。
以下示例错误地依赖于 PDO::exec() 的返回值,其中影响 0 行的语句会导致对 die() 的调用
<?php
$db->exec() or die(print_r($db->errorInfo(), true)); // 错误
?>
如果属性 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_WARNING
,则发出级别为 E_WARNING
的错误。
如果属性 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_EXCEPTION
,则抛出 PDOException。
示例 #1 发出 DELETE 语句
使用没有 WHERE 子句的 DELETE 语句统计删除的行数。
<?php
$dbh = new PDO('odbc:sample', 'db2inst1', 'ibmdb2');
/* 从 FRUIT 表中删除所有行 */
$count = $dbh->exec("DELETE FROM fruit");
/* 返回已删除的行数 */
print "已删除 $count 行。\n";
?>
上面的示例将输出
Deleted 1 rows.
值得在这里注意的是,除了上面文档中给出的提示之外,使用 prepare、bind 和 execute 比多次查询语句提供了更多的好处:性能和安全性!
如果您使用 INSERT INTO ... 将一些二进制数据(例如图像文件)插入数据库,那么它可能会提高解析语句的性能,因为它保持较小(只有几个字节,而图像可能有多个 MiBytes),并且不需要转义/引用文件的二进制数据以成为一个正确的字符串值。
最后,例如,如果您希望获得一个不受 SQL 注入攻击影响的更安全的 PHP 应用程序,您_必须_考虑在每个包含数据的语句(如带有 WHERE 子句的 INSERT 或 SELECT)上使用 prepare/execute。使用 prepare、bind 和 execute 将语句代码与相关数据分开是最佳方法——快速且安全!您甚至不需要转义/引用/格式检查任何数据。
请注意,使用 MySQL,您可以使用 INSERT 检测到重复键(1 = INSERT,2 = UPDATE)
<?php
// MySQL 特定的 INSERT UPDATE-like 语法
$sql = <<<SQL
INSERT INTO customers
SET
id = {$pdo->quote($id)},
name = {$pdo->quote($name)},
address = {$pdo->quote($address)}
AS new
ON DUPLICATE KEY UPDATE
name = new.name,
address = new.address
SQL;
$result = $pdo->exec($sql);
if ($result === 1) {
// 已执行新行的 INSERT
} elseif ($result === 2) {
// 已执行现有行的 UPDATE
}
PDO::eval() 可能在使用 PDO_DBLIB 和 FreeTDS 时,即使操作成功完成,也会对某些语句(例如 CREATE TABLE)返回 `false`。因此,它不是测试操作状态的可靠方法。
PDO::errorInfo() 可用于测试 SQLSTATE 错误代码是否为 '00000'(成功)和 '01000'(成功并有警告)。
<?php
function execute(PDO $conn, $sql) {
$affected = $conn->exec($sql);
if ($affected === false) {
$err = $conn->errorInfo();
if ($err[0] === '00000' || $err[0] === '01000') {
return true;
}
}
return $affected;
}
?>
PDO::errorInfo(): https://php.net/manual/en/pdo.errorinfo.php
SQLSTATE 代码列表: http://www-01.ibm.com/support/knowledgecenter/SSGU8G_11.70.0/com.ibm.sqls.doc/ids_sqs_0809.htm
此函数不执行多查询。
要获取它,请查看 SQLITE_EXEC 注释,那里有一个 pereg 函数可以获取所有查询并执行所有查询,然后返回最后一个查询。
对于那些想要一个像 prepare/execute 一样处理参数的 exec 函数的人。您可以使用另一个函数来模拟它。
<?php
class Real_PDO extends PDO {
public function execParams($sql, $params) {
$stm = $this->prepare($sql);
$result = false;
if( $stm && $stm->execute($params) ) {
$result = $stm->rowCount();
while( $stm->fetch(PDO::FETCH_ASSOC) ) {
}
}
return $result;
}
}
?>
请记住,如果您要进行大量插入操作,您需要以手动方式进行,因为在执行(插入)多次时,准备语句会加快速度。我使用它以便我可以将所有 SQL 语句放在一个地方,并针对 SQL 注入进行自动安全引用。
如果您想知道 fetch 后的原因,请记住,一些数据库可以从 REMOVE/INSERTS 返回类似 SELECT 的数据。对于 PostgreSQL,您可以让它返回所有实际被删除的记录,或者让插入返回插入/后字段函数和 io 触发器触发后的记录,以提供标准化数据。
<?php
define("BLAH_INSERT", "INSERT INTO blah (id,data) VALUES(?,?)");
$pdo = new Real_PDO("connect string");
$data = array("1", "2");
$pdo->execParams(BLAH_INSERT, $data);
?>
您不仅不能将其用于 SELECT 语句,也不能将其用于可能返回行的任何语句。“OPTIMIZE table” 就是这样的例子(返回一些带有优化状态的行)。
如果您这样做,PDO 会因“在其他未缓冲查询处于活动状态时无法执行查询”的错误而锁定。