单纯使用聚合函数,无论是否包含 NULL,无论是否删除重复数据,都是针对表中的所有数据进行的汇总处理。如果需要先分组,然后在进行汇总处理。这时需要 GROUP BY 子句。
语法:
SELECT
<聚合函数>,
<聚合字段>
FROM
<表名>
WHERE
<筛选条件>
GROUP BY
<聚合字段>
HAVING
<分组结果对应的条件>
ORDER BY
<排序字段>;
执行顺序:
FROM —-> WHERE —-> GROUP BY —-> HAVING —-> SELECT —-> ORDER BY
GROUP BY 子句支持单个字段和多个字段分组,各字段之间用 ,
分隔。
# 查询每个工种的最高工资。
SELECT
MAX( salary ),
job_id
FROM
employees
GROUP BY
job_id;
# 查询按位置分组的个数。
SELECT
COUNT(*),
location_id
FROM
departments
GROUP BY
location_id;
# 查询邮箱中包含 a 字符的,每个部门的平均工资。
SELECT
AVG( salary ),
department_id
FROM
employees
WHERE
email LIKE '%a%'
GROUP BY
department_id;
# 查询有奖金的每个领导手下员工的最高工资。
SELECT
MAX( salary ),
manager_id
FROM
employees
WHERE
commission_pct IS NOT NULL
GROUP BY
manager_id;
如果需要对分组后的结果进行筛选的话,这时需要 HAVING 子句。
WHERE 子句 = 指定行所对应的条件。
HAVING 子句 = 指定组所对应的条件。
能够在 HAVING 子句中能够使用的 3 种要素:常数、聚合函数、GROUP BY 子句中指定的列名。
在 WHERE 子句和 HAVING 子句中都可以使用的条件,最好写在 WHERE 子句中,这和执行的速度有关系。因为在使用聚合函数对表中的数据进行聚合操作时,DBMS 内部就会进行排序处理。排序处理会大大增加机器负担,只有尽可能减少排序的行数,才能提高处理速度。通过 WHERE 子句指定条件时, 由于排序之前就对数据进行了过滤, 因此能够减少排序的数据量。 但 HAVING 子句是在排序之后才对数据进行分组。
另外的一个理由是可以对 WHERE 子句指定的条件创建索引。
# 查询哪个部门的员工个数 > 2。
# 1. 查询每个部门的员工个数。
# 2. 根据 1 的结果进行筛选,员工个数 > 2。
SELECT
COUNT(*),
department_id
FROM
employees
GROUP BY
department_id;
HAVING COUNT(*) > 2;
# 查询每个工种有奖金的员工的最大工资 > 12000 的工种编号和最高工资。
# 1. 每个工种有奖金的员工的最高工资。
# 2. 根据 1 的结果进行筛选,最高工资 > 12000。
SELECT
MAX( salary ),
job_id
FROM
employees
WHERE
commission_pct IS NOT NULL
GROUP BY
job_id
HAVING
MAX( salary ) > 12000;
# 查询领导编号 > 102 的每个领导手下的最低工资 > 5000 的领导编号是哪个。
# 1. 查询每个领导手下的员工固定最低工资。
# 2. 添加筛选条件,编号 > 102。
# 3. 添加筛选条件,最低工资 > 5000。
SELECT
min( salary ),
manager_id
FROM
employees
WHERE
manager_id > 102
GROUP BY
manager_id
HAVING
MIN( salary ) > 5000
ORDER BY email;