count(*)统计所有行,安全可靠;count(字段)跳过NULL值,适用于统计非空数量;带WHERE的条件过滤应在聚合前进行,避免索引失效和性能问题。

count(*) 和 count(字段) 的行为差异
直接用 count(*) 统计行数最安全,它不关心字段是否为 NULL;而 count(列名) 会跳过该列值为 NULL 的所有行。比如用户表里有 100 条记录,但 email 字段有 12 个是 NULL,那么 count(email) 返回 88,count(*) 才是 100。
常见误用场景:想查“有多少用户填了手机号”,却写了 count(*),结果得到总注册数而非有效填写数 —— 这时必须明确用 count(phone)(前提是 phone 允许为 NULL)。
带 WHERE 条件的 count 查询写法
统计不是简单套函数,条件过滤必须写在 WHERE 子句里,不能靠 HAVING(除非已分组)。例如查“2024 年注册的活跃用户数”:
SELECT COUNT(*) FROM users WHERE status = 'active' AND created_at >= '2024-01-01';
注意点:
-
WHERE在聚合前过滤,性能好;HAVING是聚合后筛,开销大且逻辑易错 - 时间字段比较要确认类型:若
created_at是DATETIME,用字符串比较没问题;若是TIMESTAMP且有时区,可能需用FROM_UNIXTIME()或显式转换 - 避免在
WHERE中对字段用函数(如YEAR(created_at) = 2024),会导致索引失效
count 配合 GROUP BY 做分组统计
需要按类别看数量时,GROUP BY 是必选项,漏掉会只返回一行汇总结果。例如统计各城市用户数:
SELECT city, COUNT(*) AS user_count FROM users WHERE city IS NOT NULL GROUP BY city ORDER BY user_count DESC;
关键细节:
-
GROUP BY列必须出现在SELECT中(除非用 MySQL 5.7+ 的ONLY_FULL_GROUP_BY关闭模式) - 如果
city有空值,GROUP BY会把所有NULL归为一组,通常要加WHERE city IS NOT NULL过滤 - 别名(如
user_count)不能在WHERE中引用,但可在HAVING中用,比如加HAVING user_count > 100
count 查询慢?先看执行计划和索引
COUNT(*) 在 InnoDB 表上并不总是全表扫描 —— 如果有合适索引且无 WHERE 条件,MySQL 可能用二级索引的叶子节点行数估算(但不绝对,尤其大表)。真正拖慢的往往是:
- 没加
WHERE却查大表的COUNT(*):InnoDB 必须遍历聚簇索引或某二级索引,数据量越大越慢 -
WHERE条件没走索引:比如WHERE CONCAT(name, '') = 'John'或WHERE DATE(create_time) = '2024-01-01' - 用了
JOIN后再COUNT(*):可能产生笛卡尔积,行数暴增
排查方法:在语句前加 EXPLAIN,重点看 type 是否为 index 或 range,rows 是否远小于表总行数。
复杂点在于:有些业务场景下,精确实时计数本身就不该由数据库承担 —— 比如百万级商品的销量总数,更适合用 Redis 计数器或异步更新汇总表。










