COUNT(*)统计所有行数,不忽略NULL;COUNT(字段名)仅统计该字段非NULL值,若空值多则结果显著偏小。

为什么 COUNT(*) 和 COUNT(字段名) 结果差很多
COUNT(<em>)</em> 统计的是行数,不管字段是不是 NULL;而 COUNT(字段名) 只统计该字段非 NULL 的值。如果字段本身空值多,COUNT(字段名) 就会明显小于 COUNT()。
- 查字段空值比例:运行
SELECT COUNT(*), COUNT(字段名) FROM 表名;,对比两个数字差异 - 想统计“所有记录条数”,必须用
COUNT(*),别被字段名带偏 - 如果业务上把空字符串
''或字符串'NULL'也当无效值,COUNT(字段名)仍会把它算进去——它只跳过真正的 SQLNULL - 真要排除空字符串+
NULL,得写成:COUNT(CASE WHEN 字段名 IS NOT NULL AND 字段名 != '' THEN 1 END)
关联查询后 COUNT(*) 突然变大,不是数据错了而是膨胀了
一对多关联(比如用户表 LEFT JOIN 订单表)会让主表一行,在结果集中变成多行(一个用户对应 N 个订单 → 出现 N 行)。这时 COUNT(*) 数的是“连接后的总行数”,不是主表原始记录数。
- 常见错误写法:
SELECT COUNT(<em>), u.name FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id;</em>—— 这里的COUNT()是每个用户的订单数,不是用户数 - 要统计“有多少用户有订单”,得用:
COUNT(DISTINCT u.id) - 要统计“总用户数(含无订单的)”,不能在关联后直接
COUNT(<em>)</em>,得单独查主表,或用子查询/窗口函数:COUNT() OVER()配合去重逻辑
用了 DISTINCT 还不准?先看它作用在谁身上
COUNT(DISTINCT 字段名) 看似安全,但容易误判业务含义:
-
DISTINCT对NULL的处理是“多个NULL算一个”,但有些场景你希望NULL单独计为一类(比如“未填性别”需要独立统计),此时得改用:COUNT(DISTINCT COALESCE(字段名, 'NULL')) - 多字段去重时,
COUNT(DISTINCT a, b)是按组合去重,但 MySQL 5.7 及更早版本不支持多列DISTINCT,会报错;MySQL 8.0+ 和 PostgreSQL 支持,但 SQLite 不支持 - 如果关联后做
COUNT(DISTINCT 主键),多数情况能救回来,但要注意:LEFT JOIN 中若关联表无匹配行,主键仍存在,DISTINCT有效;INNER JOIN 则可能漏掉主表孤立记录
关联后的 count 错误,根源几乎都在“没想清楚你要数什么”——是物理行数?主表实体数?还是某字段的有效取值种类?一旦混淆作用对象和作用范围,再加几个 DISTINCT 也压不住逻辑漏洞。

















