count(列名) 统计该列非null的行数,不过滤整行,仅跳过null值;与count(*)不同,后者统计所有行。

为什么 COUNT(列名) 能统计非空记录数
COUNT(列名) 会自动跳过 NULL 值,只计算该列值不为 NULL 的行数。它和 COUNT(*) 的行为本质不同:COUNT(*) 统计所有行(包括某列是 NULL 的行),而 COUNT(列名) 统计的是“该列有实际值”的行数。
常见误解是以为它在过滤整行——其实不是。只要指定列非 NULL,哪怕其他列全是 NULL,这行也会被计入。
按类别分组统计非空数量:GROUP BY + COUNT(列名)
典型场景是带分类维度的业务表,比如商品表有 category 和 price,你想知道每个品类下有多少个标了价格的商品(即 price IS NOT NULL 的数量)。
- 必须配合
GROUP BY category,否则聚合结果只有一行 - 不能写成
COUNT(DISTINCT 列名)——那是去重计数,和非空无关 - 如果想同时看总数和非空数,可并列写
COUNT(*)和COUNT(列名)
SELECT category, COUNT(*) AS total, COUNT(price) AS price_not_null FROM products GROUP BY category;
COUNT(列名) 和 COUNT(*) 在 NULL 多的表里性能差异不大
很多人担心 COUNT(列名) 会额外判断 NULL 导致变慢,实际在主流数据库(PostgreSQL、MySQL 8.0+、SQL Server)中,优化器通常复用同一行计数逻辑,只是最后根据列是否可空做轻量过滤。真正影响性能的是是否走索引、数据量大小、是否需要临时表排序。
- 如果该列上有非空约束(
NOT NULL),优化器可能直接等价于COUNT(*) - 如果列允许 NULL 但实际几乎全非空,
COUNT(列名)也不会明显变慢 - 别为了“避免 NULL 判断”而改用
WHERE 列名 IS NOT NULL再COUNT(*)——多一次扫描反而更差
容易踩的坑:别把表达式或常量当列名传给 COUNT
COUNT(1)、COUNT(*)、COUNT('x') 都等价于全行计数,它们**不检查 NULL**;只有明确传一个**基表中的列名**(如 COUNT(email)),才会触发 NULL 过滤逻辑。
- 错误写法:
COUNT(IF(email != '', email, NULL))—— 多余且不可读,直接用COUNT(email)即可 - 错误写法:
COUNT(COALESCE(email, ''))—— 空字符串不是NULL,这会导致本该排除的空值被计入 - 注意字段别名不能用于
COUNT(),如SELECT email AS e FROM t GROUP BY e HAVING COUNT(e) > 0是错的,得写COUNT(email)
真正要小心的,是误把业务语义上的“空”(比如空字符串、'N/A'、0)当成 SQL 意义上的 NULL——COUNT(列名) 对这些一概不管,只认 NULL。










