COUNT(*) 与 COUNT(col) 的区别
两者都能出现在同一条 SQL 里,但含义不同:COUNT(*) 统计结果集的行数,包含该行所有列都为 NULL 的情况;COUNT(col) 只统计该列非 NULL 的值个数,遇到 NULL 不计入。这个差异是数据质量排查的高频入口——COUNT(*) 减 COUNT(col) 就是该列的缺失行数。需要注意 COUNT(col) 在列可空的表上永远小于等于 COUNT(*),两者相等说明该列没有空值。
基本信息
| COUNT(*) | 统计行数,包含所有 NULL 行 |
|---|---|
| COUNT(col) | 统计该列非 NULL 的值个数 |
| COUNT(1) | 与 COUNT(*) 结果相同,写法差异而已 |
| 无匹配行 | 两者都返回 0 |
| 常见组合 | COUNT(*) 减 COUNT(col) 得到缺失行数 |
| 性能差异 | 小表无感;大表上 COUNT(col) 需判断列是否可空,可能更慢 |
典型使用场景
- 统计总数:列表分页显示总条数
- 统计填充率:某列有多少行真正有值
- 数据质量排查:找出 NULL 比例异常的列
- 报表口径统一:明确指标是按行数还是按有值的行数
- 去重与计数验证:配合 COUNT(DISTINCT) 交叉核对
- 接口返回校验:确认字段是否有缺失
示例
示例
SELECT COUNT(*) AS all_rows, # 总行数
COUNT(phone) AS has_phone, # 手机号非空的行数
COUNT(*) - COUNT(phone) AS missing_phone, # 缺失行数
COUNT(phone) * 100.0 / COUNT(*) AS fill_rate # 填充率
FROM users;
-- 两者相等,说明该列没有空值
SELECT COUNT(*), COUNT(id) FROM users;
-- 去重计数是第三种口径,不要与上面两个混用
SELECT COUNT(DISTINCT user_id) FROM orders;三者口径对照
| 写法 | 统计对象 | 为 0 的情况 |
|---|---|---|
| COUNT(*) | 所有行 | 结果集为空 |
| COUNT(col) | col 非 NULL 的行 | 结果集为空或该列全为 NULL |
| COUNT(DISTINCT col) | col 去重后的非空值 | 结果集为空或该列全为 NULL |
| COUNT(1) | 所有行 | 结果集为空 |
| SUM(col IS NULL) | NULL 行数(MySQL) | 无 NULL 行 |
在可空列上,COUNT(col) 一定小于等于 COUNT(*),两者差值是缺失量;这个性质可以当作快速体检手段。
接口统计口径要写清楚:同一个「用户数」指标,按行数算与按去重算可能相差一个量级。
COUNT(*) 在 InnoDB 上仍需遍历索引统计,大表无条件计数代价高;只需要判断存在性时改用 EXISTS。
使用要点
- 统计前先确认列是否可空,再决定用哪种计数
- 一次查询同时输出多种计数,便于交叉核对
- 报表与监控里区分总行数与有效行数两个指标
- 大表统计优先考虑覆盖索引或缓存结果
- 把填充率做成日常监控指标,及时发现上游字段缺失
- 跨库写法保持一致,避免同一份报表在不同库口径不同