COUNT(*)统计所有行(含NULL),COUNT(字段)仅统计该字段非NULL的行;GROUP BY字段须与SELECT中非聚合字段完全一致;左连接查空部门人数应用COUNT(字段)并把过滤条件移至ON子句。

GROUP BY 后 COUNT(*) 和 COUNT(字段) 的区别必须分清
很多人写 COUNT(*) 和 COUNT(emp_id) 得到不同结果,却不知道为什么。核心在于:前者统计行数(包括 NULL 行),后者只统计该字段非 NULL 的行。部门表里如果某条记录的 emp_id 是 NULL,它会被 COUNT(emp_id) 忽略,但不会被 COUNT(*) 忽略。
实际场景中,员工表主键通常不为空,所以多数情况下两者结果一致;但一旦涉及左连接(比如查部门+员工,某些部门没员工),emp_id 就可能为 NULL,这时必须用 COUNT(*) 才能准确反映“该部门关联了多少条记录”——也就是你真正想统计的“人数”逻辑。
- 要统计“每个部门有多少员工”,一律用
COUNT(*),前提是JOIN或WHERE已过滤掉无效关联 - 如果字段本身允许
NULL且你想排除空值(比如统计“填了邮箱的员工数”),才用COUNT(email) -
COUNT(1)和COUNT(*)在所有主流数据库中行为一致,性能无差别,可互换
GROUP BY 字段必须和 SELECT 中非聚合字段完全一致
常见报错:ERROR: column "dept_name" must appear in the GROUP BY clause or be used in an aggregate function。这不是语法警告,是强制规则。SQL 要求:只要 SELECT 里写了非聚合字段(比如 dept_name),它就必须出现在 GROUP BY 列表中,一个字符都不能少,也不能靠别名绕过。
例如,不能写 SELECT UPPER(dept_name), COUNT(*) FROM emp GROUP BY dept_name —— 因为 UPPER(dept_name) 不等于 dept_name,数据库无法确认分组依据是否稳定。
- 正确写法是
SELECT UPPER(dept_name), COUNT(*) FROM emp GROUP BY UPPER(dept_name) - 如果原始字段名是
department_name,而你SELECT department_name AS dept,GROUP BY 仍得写department_name,不能写dept - MySQL 旧版本(5.7 之前)有宽松模式,但别依赖——标准 SQL 和新版本都严格校验
空部门没出现在结果里?检查 JOIN 类型和 WHERE 条件
想列出所有部门(含人数为 0 的),但 SELECT dept.name, COUNT(*) FROM dept JOIN emp ON dept.id = emp.dept_id GROUP BY dept.name 只返回有员工的部门。问题出在 JOIN:内连接天然丢弃无匹配的部门行。
此时必须改用 LEFT JOIN,并把员工过滤条件从 WHERE 移到 ON 子句,否则 WHERE emp.status = 'active' 会把空部门行再次筛掉(因为 emp.status 是 NULL,不满足条件)。
- 正确写法:
SELECT dept.name, COUNT(emp.id) FROM dept LEFT JOIN emp ON dept.id = emp.dept_id AND emp.status = 'active' GROUP BY dept.name - 用
COUNT(emp.id)而不是COUNT(*),是因为左连接后空部门的emp.id是NULL,COUNT(emp.id)自然得 0;而COUNT(*)会算上部门行本身,得 1 - 如果部门表本身有冗余或重复数据,先
DISTINCT或去重再关联,否则人数会被放大
COUNT 配合 HAVING 筛选分组结果时,别在 WHERE 里写聚合条件
想查“员工数超过 5 人的部门”,写成 WHERE COUNT(*) > 5 会直接报错。因为 WHERE 执行在分组前,此时 COUNT(*) 还没计算。
必须用 HAVING —— 它专为筛选分组后的结果而设,执行时机在 GROUP BY 之后、ORDER BY 之前。
- 正确写法:
SELECT dept_name, COUNT(*) FROM emp GROUP BY dept_name HAVING COUNT(*) > 5 -
HAVING可以用别名(如HAVING cnt > 5),但仅限 PostgreSQL;MySQL 和 SQL Server 不支持,建议直接写函数 - 如果同时需要全局筛选(如只看 2024 年入职员工)和分组筛选(人数 > 5),
WHERE写年份条件,HAVING写人数条件,顺序不能颠倒
最常被忽略的是左连接 + COUNT 的组合行为:空部门的计数是否为 0,取决于你用的是 COUNT(*) 还是 COUNT(字段),以及过滤条件放在 ON 还是 WHERE。这两个细节一错,人数就对不上。










