COUNT(*)统计结果不会比实际行数多,所谓“变多”实为JOIN等操作导致结果集膨胀,COUNT统计的是当前结果集行数而非原表;COUNT(字段)跳过NULL,COUNT(DISTINCT 字段)亦不计NULL,二者均不可能多算。

COUNT(*) 返回的行数不可能比实际数据多——如果你观察到“COUNT 后数量比实际多”,一定是你没在数同一份数据,或者对“实际数据”的定义有偏差。
为什么 COUNT(DISTINCT col) 会少于预期,但绝不会“多”?
这是最常见的误解源头:你以为在数“有多少条记录”,实际执行的是 COUNT(DISTINCT suitName),而结果比手动 GROUP BY suitName 数出来的值小(比如 22 vs 23)。这不是“多了”,是 COUNT(DISTINCT ...) 把 NULL 值直接跳过了——DISTINCT 不会把 NULL 当作一个独立值,所有 NULL 被合并为“无”,不参与去重计数。
-
COUNT(DISTINCT col)只统计非NULL的唯一值,NULL不计入也不去重 - 你用
SELECT suitName FROM race_goods GROUP BY suitName看到的 23 行里,很可能包含 1 行suitName IS NULL——它出现在结果集中,但被COUNT(DISTINCT suitName)忽略了 - 验证方法:
SELECT COUNT(*) FROM race_goods WHERE suitName IS NULL,如果返回 1,就印证了这点
COUNT(字段) 在 JOIN 场景下为何看起来“变多”?
不是 COUNT 本身变多,而是 JOIN 把 1 行主表数据展开成多行,COUNT(字段) 统计的是这些展开后的行中该字段非 NULL 的数量。
- 例如:
LEFT JOIN orders ON users.id = orders.user_id,1 个用户有 3 笔订单 → 产生 3 行 -
COUNT(orders.id)返回 3,不是“用户数”,是“订单关联成功且 id 非 NULL 的行数” -
COUNT(*)在同一查询中也会返回 3,不是“多了”,是 JOIN 后结果集本来就是 3 行 - 真正想统计用户数?该用
COUNT(DISTINCT users.id);想统计订单数?直接COUNT(orders.id)即可,但要确保orders.id是主键(天然非 NULL)
别被 COUNT(1) 或 COUNT('x') 的写法误导
COUNT(1)、COUNT('hello') 和 COUNT(*) 在语义和结果上完全等价,都统计所有行(含 NULL),它们不会导致数量“变多”。但要注意:
- 某些旧版 MySQL + MyISAM 引擎下,
COUNT(*)可能读元数据缓存,而COUNT(1)强制走引擎层扫描——理论上后者更慢,但结果一致 - 如果写成
COUNT(col)却误以为它等于总行数,而该列允许NULL,那结果只会“变少”,不可能变多 - 唯一可能“变多”的情况:你在子查询或 CTE 中做了重复展开(如 CROSS JOIN、未加限制的 JOIN),再套
COUNT(*)——问题出在逻辑错误,不在COUNT函数本身
真正容易被忽略的点是:COUNT 的输入是“当前查询生成的结果集”,不是原表。JOIN、GROUP BY、WHERE 这些操作先改写结果集,COUNT 再统计——所以别盯着函数看,先确认你拿到的中间结果是不是你想要的那张“临时表”。

















