首页/数据/SQL 速查/受影响行数:changed 与 matched 的区别
变更与事务52 条

受影响行数:changed 与 matched 的区别

确认 DML 实际影响:MySQL 默认返回“发生变化”的行数,开启 CLIENT_FOUND_ROWS 后返回“匹配到”的行数;PostgreSQL 返回处理行数。

SQL 速查数据更新于 2026-10-13

两个口径是什么

执行 INSERT/UPDATE/DELETE 后拿到的受影响行数,其实有两套口径。MySQL 默认返回 changed:只有值真的发生变化才计数,把行改成和原来一样则计 0;连接层带上 CLIENT_FOUND_ROWS 标记后返回 found(匹配到的行数),匹配即计数。PostgreSQL 返回匹配并处理的行数,值相同也照样计数。

基本信息

MySQL 默认changed 口径:值发生变化的行才计数
MySQL found 口径需连接带上 CLIENT_FOUND_ROWS 标记
PostgreSQL返回匹配并处理的行数,值相同也计数
取值方式MySQL 用客户端 affected_rows 或 ROW_COUNT();PG 用命令标签或 GET DIAGNOSTICS
典型用途幂等脚本判断存在性、判断是否真的发生变更

典型使用场景

  • 幂等脚本判断记录是否存在:需要 found 口径(匹配即计数)
  • 判断是否真的发生变更以决定后续动作:需要 changed 口径
  • 乐观锁与版本号校验:期望受影响行数等于 1,多了少了都说明并发异常
  • 数据修复脚本核对命中行数,确认改动范围符合预期
  • 批量迁移统计进度:口径统一后进度才可信

MySQL 写法

MySQL 写法
UPDATE t SET status = 'x' WHERE id = 1;
SELECT ROW_COUNT();   -- 默认 changed 口径:值没变就返回 0

-- 想要 found 口径需要连接层带 CLIENT_FOUND_ROWS 标记
-- 各驱动参数名不同,改前先确认当前驱动的默认值
-- 例如 Connector/J 的 useAffectedRows 就决定是否带这个标记
-- 值相同也期望“命中 1 行”的脚本,必须用 found 口径

PostgreSQL 写法

PostgreSQL 写法
UPDATE t SET status = 'x' WHERE id = 1;
-- 命令标签直接打印:UPDATE 1,匹配到就算

-- 在函数或存储过程里取行数
GET DIAGNOSTICS n = ROW_COUNT;   -- 最近一条语句处理的行数
RAISE NOTICE 'affected=%', n;     -- 调试时打印出来

两库返回值对照

情形MySQL changed 口径PostgreSQL
匹配 1 行且值变化11
匹配 1 行但值未变01
匹配 0 行00
匹配 3 行、其中 1 行值变化13
幂等脚本要区分两种判断:判「记录是否存在」用 found 口径,判「是否真的发生变更」用 changed 口径。把 changed 计 0 直接当成记录不存在,是最常见的误判。
PostgreSQL 的 ROW_COUNT 是「处理过的行数」,UPDATE 值没变同样计数,因此更接近 found 口径,不能当作「数据变了」的证据。

易错点

  • 把 affected=0 当成记录不存在:值相同未变化时也是 0,会误判并触发重复插入;要么用 found 口径,要么先 SELECT 判断存在性
  • 忽略驱动与框架的默认口径:连接标记不同会导致同一句 UPSERT 返回值不同,升级驱动后统计口径可能悄悄改变
  • 批量脚本用受影响行数做进度统计而口径不统一:进度虚高或虚低,先固定口径再统计
  • 用 PostgreSQL 的 ROW_COUNT 判断「是否变更」:值未变也计数,会误以为数据已经更新
  • 把 INSERT ... ON DUPLICATE KEY UPDATE 的返回值当成插入行数:它按插入/更新/无变化返回不同值,语义与其他 DML 不一致

数据来源与参考

  • MySQL 8.0 Reference Manual · UPDATE
  • PostgreSQL Documentation · Command Tags
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌