COUNT()统计所有行且性能最优,COUNT(col)仅统计非NULL值;GROUP BY需严格遵循ONLY_FULL_GROUP_BY规则;大表避免直接COUNT(),可用估算或业务计数器;分桶用ROUND/FLOOR更可靠。

COUNT(*) 和 COUNT(col) 的区别必须分清
查总数时别想当然用 COUNT(col) 代替 COUNT(*)。前者只算该列非 NULL 的行,如果字段允许 NULL(比如 updated_at 或 referral_code),结果会偏少;后者统计所有行,包括全 NULL 行,且 InnoDB 下通常走最小索引,性能最优。
-
COUNT(*)是安全、快、准的默认选择 -
COUNT(col)只在明确要排除 NULL 时才用,比如“统计已激活用户数”对应COUNT(active_at) - MySQL 8.0+ 对
COUNT(1)和COUNT(*)优化一致,没必要刻意写COUNT(1)
用 GROUP BY 看字段值分布时注意 SQL 模式限制
MySQL 8.0 默认开启 ONLY_FULL_GROUP_BY,SELECT 列里只要出现没被聚合也没被 GROUP BY 的字段,就会报 ERROR 1055。常见翻车点:
- 写
SELECT status, user_id, COUNT(*) FROM orders GROUP BY status→ 报错,user_id未聚合也未分组 - 正确写法是只保留分组字段和聚合表达式:
SELECT status, COUNT(*) AS cnt FROM orders GROUP BY status - 若真要附带某条样本记录(如最早下单的
user_id),得用MIN(user_id)或窗口函数,不能裸写字段名
大表上避免直接 SELECT COUNT(*) 做实时统计
对千万级表执行 SELECT COUNT(*) FROM big_table 可能锁表、慢、影响线上查询。替代方案有:
- 查
INFORMATION_SCHEMA.TABLES:快速拿到估算行数,但TABLE_ROWS是 InnoDB 采样值,误差常达 10%~20% - 用
ANALYZE TABLE更新统计信息后重查,能小幅提升估算精度,但不改变本质是估算的事实 - 业务层维护计数器(如用 Redis + MySQL binlog 同步),适合需要精确、高频读取的场景
- 对空表要注意:MySQL 5.6 中
TABLE_ROWS返回NULL,需加IFNULL(table_rows, 0)
分布区间统计别硬写一堆 CASE WHEN
按数值范围分桶(比如响应时间、金额、年龄)时,手写十几段 CASE WHEN 易出错、难维护。更稳的做法:
- 用
ROUND(value / 1000) * 1000或FLOOR(value / 500) * 500做整型分桶,再GROUP BY - 对离散程度高的字段(如用户 ID),优先考虑
COUNT(DISTINCT col),但注意它会建哈希表,大表高基数字段可能 OOM - 想看字段值频次 Top N?直接
SELECT col, COUNT(*) FROM t GROUP BY col ORDER BY COUNT(*) DESC LIMIT 10
直方图(ANALYZE TABLE t UPDATE HISTOGRAM ON col)只影响优化器选执行计划,不提供可查的分布数据——别指望它返回“每个区间的行数”。真要精确分布,还是得靠 GROUP BY 或应用层采样。


















