count(*) 和 count(字段) 的行为差异
直接用
count(*)统计行数最安全,它不关心字段是否为 NULL;而
count(列名)会跳过该列值为
NULL的所有行。比如用户表里有 100 条记录,但
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 计数器或异步更新汇总表。
