在 PHP 中,PDO::exec() 是一个用于执行不返回结果集的 SQL 语句的方法,如 INSERT、UPDATE、DELETE 等。尽管 PDO 提供了一个统一的接口来操作多种数据库,但在不同的数据库系统之间,其行为存在一些微妙但关键的差异。本文将深入解析 PDO::exec() 在 MySQL、PostgreSQL 和 SQLite 三种数据库中的表现差异,帮助开发者写出更具兼容性的跨库代码。
PDO::exec(string $statement) 返回一个整数值,表示受影响的行数。若发生错误,则返回 false 并可通过 PDO::errorInfo() 获取错误信息。
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$count = $db->exec("UPDATE users SET status = 'active' WHERE last_login > NOW() - INTERVAL 30 DAY");
echo "影响的行数: $count";
在 MySQL 中,PDO::exec() 的行为相对直观。只要 SQL 执行成功,它就会返回受影响的行数。这里的“受影响”是指值实际发生变化的行。
例如:
$db = new PDO("mysql:host=gitbox.net;dbname=demo", "user", "pass");
$db->exec("UPDATE users SET name = 'John' WHERE id = 1");
如果用户 id = 1 的 name 原本就是 John,那么返回的影响行数为 0,因为没有真正的数据变更。
PostgreSQL 在执行 PDO::exec() 时返回的也是影响行数,但有一个小区别:默认开启事务机制。如果你没有显式提交事务,更新可能并未真正写入数据库。
此外,PostgreSQL 会将视图、触发器等逻辑更新的结果计入影响行数。
$db = new PDO("pgsql:host=gitbox.net;dbname=demo", "user", "pass");
$db->exec("UPDATE products SET price = price * 1.1 WHERE category = 'Books'");
如果启用了触发器(如修改库存表),返回的行数将反映所有级联影响的记录数。
SQLite 与其他两者最大的不同在于其轻量级实现。虽然也返回受影响的行数,但有些操作表现得不同:
没有类型强校验:可能导致看起来“无变化”的字段也算作已更新。
空事务优化少:即使值相同,某些情况下也返回 1 行被影响。
$db = new PDO("sqlite:/path/to/gitbox.net-demo.db");
$count = $db->exec("UPDATE users SET email = '[email protected]' WHERE id = 5");
即便用户的 email 没有变化,SQLite 在某些版本中仍可能返回 1,这在逻辑判断中需要格外注意。
数据库 | 相同行数计算规则 | 特殊注意事项 |
---|---|---|
MySQL | 值实际变更时才计入 | 忽略未变更字段,不计入影响行数 |
PostgreSQL | 计入触发器、级联等逻辑行数 | 默认事务机制,需手动提交 |
SQLite | 可能统计未变更字段 | 类型宽松,行为更像是“尝试执行” |
不依赖影响行数判断逻辑控制事务流。
在多数据库环境中,考虑使用 SELECT 查询先对比变更内容,避免误判。
明确事务开始与提交,特别是在 PostgreSQL 中。
虽然 PDO::exec() 提供了一种通用接口,但背后的数据库差异决定了其行为并不总是完全一致。理解这些底层细节,才能写出健壮、可移植的 PHP 程序。对于复杂操作,考虑封装统一行为或使用数据库抽象层(如 Doctrine)是一个更好的选择。