mysql如何统计行数_mysql count函数操作说明

6次阅读

count()和count(1)统计所有行(含NULL),执行一致;count(字段)跳过该字段为NULL的行;InnoDB中count()需遍历索引,无行数缓存,大表慎用。

mysql如何统计行数_mysql count函数操作说明

count(*)、count(1)、count(字段) 到底有什么区别

三者统计逻辑不同,但 count(*)count(1) 在 InnoDB 下行为完全一致,都是统计满足 WHERE 条件的所有行(含全 NULL 行);而 count(字段) 会跳过该字段值为 NULL 的行。

  • count(*):语义最清晰,mysql 优化器会自动选择成本最低的索引(如主键)遍历计数,不依赖字段是否可空
  • count(1):本质是常量表达式,和 count(*) 执行计划、性能无差别,纯属写法偏好
  • count(age):只统计 age IS NOT NULL 的行,若该字段大量为 NULL,结果会明显小于 count(*)

为什么加了索引,count(*) 还很慢?

因为 InnoDB 引擎下,count(*) 不像 MyISAM 那样直接读取表元数据中的“行数缓存”,而是必须扫描索引树——哪怕只是扫主键索引,百万级数据仍需 IO 和遍历开销。

  • 有主键时,MySQL 优先用主键索引(type=index),比全表扫描快,但仍是 O(n)
  • 没有主键、只有普通索引时,可能选最小的非空索引,但若索引列允许 NULL,部分版本可能退化为全表扫描
  • 千万级大表慎用 count(*) 实时查总数,考虑用缓存或异步更新的统计表替代

统计去重数或带条件的行数,怎么写才不出错?

别硬套 count() 去实现条件计数,容易漏 NULL 或逻辑翻车。

  • 要统计「年龄大于 25 的人数」:直接用 count(*) + WHERE age > 25,别写 count(IF(age > 25, 1, NULL)) —— 多余且易错
  • 要统计「不同部门数量」:用 count(DISTINCT department),注意该写法自动忽略 department IS NULL 的行
  • 要统计「有邮箱邮箱不重复的人数」:count(DISTINCT c_email) 即可,无需额外 WHERE c_email IS NOT NULLDISTINCT 本身已跳过 NULL)

GROUP BY 配合 count() 时最容易踩的坑

常见错误是误以为 count(*) 会按分组“过滤掉 NULL”,其实它统计的是每组内所有行,包括该组中某列为 NULL 的记录。

  • 比如 select gender, count(*) FROM users GROUP BY gender,如果 gender 有 NULL 值,就会单独产生一行 NULL | 12
  • 想排除 NULL 分组?得显式写 WHERE gender IS NOT NULL
  • 想统计每组里「name 不为空的人数」?用 count(name),不是 count(*) —— 后者统计整组行数,前者只算 name 非空的行

实际开发中,最常被忽略的是:InnoDB 的 count(*) 没有“瞬时快照”属性,它反映的是当前事务隔离级别下可见的行数,RR 级别下可能因 MVCC 读到旧版本导致计数偏少——这不是 bug,是设计使然。

text=ZqhQzanResources