本篇我们介绍 Oracle 中的数据分组汇总和分组后的数据过滤。
聚合函数(aggregate function)可以针对一组数据进行汇总并返回一个结果。例如,AVG(x) 可以计算一组 x 的平均值。常见的聚合函数包括:
我们首先来看一个 AVG 函数的例子:
SELECT AVG(salary) AS "平均月薪"
FROM employee;
平均月薪|
-------|
9832|
以上查询返回了全体员工的平均月薪。
关于聚合函数,我们需要注意两点:
DISTINCT
选项可以在计算之前排除重复数据;例如:
SELECT COUNT(*) AS "员工总数",
COUNT(DISTINCT sex) AS "不同性别",
COUNT(bonus) AS "拥有奖金"
FROM employee;
员工总数|不同性别|拥有奖金|
-------|-------|--------|
25| 2| 9|
其中,COUNT(*) 返回了所有的记录数(员工总数),COUNT(DISTINCT sex) 返回了性别的种类,COUNT(bonus) 返回了拥有奖金的员工数量。
MIN 和 MAX 函数分别返回数据中的最小值和最大值,SUM 函数计算所有数据的和值。例如:
SELECT MIN(salary) AS "最低月薪",
MAX(salary) AS "最高月薪",
SUM(salary) AS "月薪总计"
FROM employee;
最低月薪|最高月薪 |月薪总计 |
-------|--------|----------|
4000| 30000| 245800|
LISTAGG 函数可以将一组字符串通过指定分隔符进行合并,例如:
SELECT LISTAGG(emp_name, ';') AS "员工列表"
FROM employee
WHERE dept_id = 1;
员工列表 |
-------------|
刘备;关羽;张飞|
LISTAGG 函数还提供了排序、去重、合并结果超长处理等功能,具体可以参考这篇文章。
📝除了以上常见的聚合函数之外,Oracle 还支持例如方差函数、标准差函数等更多的聚合函数,需要时可以参考官方文档。
在前面的示例中,我们将所有的数据作为一个整体(分组),聚合函数只返回了一个结果。接下来,我们结合GROUP BY
子句,将数据分成不同的组,然后分别计算各个组内的数据汇总。
假如我们想要知道每个部门的平均薪水,而不是所有员工的平均值,可以使用以下语句:
SELECT dept_id AS "部门编号",
AVG(salary) AS "平均月薪"
FROM employee
GROUP BY dept_id;
部门编号|平均月薪 |
-------|-----------------------------------------|
1| 26666.6666666666666666666666666666666667|
2| 13166.6666666666666666666666666666666667|
3| 9000|
4|7577.777777777777777777777777777777777778|
5| 5012.5|
其中,GROUP BY
子句指定了按照部门进行分组,然后通过 AVG 函数计算每个部门的平均薪水。
如果GROUP BY
分组字段存在 NULL 值,多个 NULL 值将被分为一个组。例如,以下语句按照不同奖金值统计员工的数量:
SELECT bonus AS "奖金",
COUNT(*) AS "员工数量"
FROM employee
GROUP BY bonus;
奖金 |员工数量|
-----|-------|
8000| 1|
1500| 1|
5000| 2|
| 16|
6000| 1|
10000| 3|
2000| 1|
从查询结果可以看出,16 个员工没有奖金;但他们都被分组同一个组中,而不是多个不同的组。
在使用分组汇总时,一个常见的错误就是SELECT
列表中包含了既不是聚合函数、也不属于GROUP BY
子句的字段,例如:
-- 错误示例
SELECT dept_id,
emp_name,
AVG(salary)
FROM employee
GROUP BY dept_id;
SQL 错误 [979] [42000]: ORA-00979: 不是 GROUP BY 表达式
以上语句的错误在于 emp_name 既不是分组字段,也不是聚合函数。查询按照部门进行分组,但是每个部门包含多个员工,数据库无法知道需要显示哪个员工的姓名。这是一个逻辑错误。
我们不仅可以基于一个字段进行分组,也可以依据多个字段将数据分成更多的组。例如,以下语句同时按照部门和性别统计员工的数量:
SELECT dept_id AS "部门编号",
sex AS "性别",
COUNT(*) AS "员工数量"
FROM employee
GROUP BY dept_id, sex;
部门编号|性别|员工数量|
-------|----|-------|
1|男 | 3|
2|男 | 3|
4|女 | 1|
4|男 | 8|
5|男 | 8|
3|女 | 2|
在 SQL 查询中,如果同时存在WHERE
子句和GROUP BY
子句,要求WHERE
子句出现在GROUP BY
子句之前。因此,WHERE
子句无法对分组后的结果进行过滤。例如,以下错误示例想要查找员工数量大于 5 人的部门:
-- 错误示例
SELECT dept_id,
COUNT(*)
FROM employee
WHERE COUNT(*) > 5
GROUP BY dept_id;
SQL 错误 [934] [42000]: ORA-00934: 此处不允许使用分组函数
错误原因在于WHERE
子句执行时还没有进行分组计算,它只能基于分组之前的数据行进行过滤。如果需要对分组后的结果进行过滤,需要使用HAVING
子句。
如果要查看员工数量大于 10 的部门编号,可以使用HAVING
子句实现:
SELECT dept_id,
COUNT(*) AS headcount
FROM employee
GROUP BY dept_id
HAVING COUNT(*) > 5;
DEPT_ID|HEADCOUNT|
-------|---------|
4| 9|
5| 8|
查询首先按照部门进行分组,计算每个部门的员工数量;然后使用HAVING
子句过滤员工数量大于 5 人的部门。
我们可以同时使用WHERE
子句进行数据行的过滤,使用HAVING
进行分组结果的过滤。例如想要查看薪水大于 10000 的员工数量大于 2 人的部门,可以使用以下查询:
SELECT dept_id,
COUNT(*)
FROM employee
WHERE salary > 10000
GROUP BY dept_id
HAVING COUNT(*) > 2;
DEPT_ID|COUNT(*)|
-------|--------|
1| 3|
首先,WHERE
子句过滤薪水大于 10000 的所有员工;然后按照部门进行分组,计算每个部门的员工数量;最后,使用HAVING
子句过滤这类员工数量大于 2 人的部门。
最后,我们来看一下 SQL 标准的语法:
SELECT column1,
column2,
aggregate_function(column3)
FROM table_name
[WHERE conditions]
[GROUP BY column1, column2
[HAVING conditions] ]
[ORDER BY ...]
[OFFSET m {ROW | ROWS}]
[FETCH { FIRST | NEXT } [ num_rows | n PERCENT ] { ROW | ROWS } { ONLY | WITH TIES }];
对于以上各个子句,Oracle 的逻辑执行顺序为 FROM、WHERE、SELECT、GROUP BY、HAVING、ORDER BY 以及 OFFSET/FECTH。
Oracle 中的 GROUP BY 子句还支持一些高级分组选项,我们将会在第 13 篇中进行介绍。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://tonydong.blog.csdn.net/article/details/111998697
内容来源于网络,如有侵权,请联系作者删除!