《Oracle 入門教程》第 08 篇 分組匯總
作者: 不剪發(fā)的Tony老師
畢業(yè)于北京航空航天大學(xué),十多年數(shù)據(jù)庫管理與開發(fā)經(jīng)驗,目前在一家全球性的金融公司從事數(shù)據(jù)庫架構(gòu)設(shè)計。CSDN學(xué)院簽約講師以及GitChat專欄作者。csdn上的博客收藏于以下地址:https://tonydong.blog.csdn.net
文章目錄
8.1 聚合函數(shù)
8.2 數(shù)據(jù)分組
8.3 分組過濾
本篇我們介紹 Oracle 中的數(shù)據(jù)分組匯總和分組后的數(shù)據(jù)過濾。
8.1 聚合函數(shù)
聚合函數(shù)(aggregate function)可以針對一組數(shù)據(jù)進行匯總并返回一個結(jié)果。例如,AVG(x) 可以計算一組 x 的平均值。常見的聚合函數(shù)包括:
AVG - 計算一組值的平均值。
COUNT - 統(tǒng)計一組值的數(shù)量。
MAX - 計算一組值的最大值。
MIN - 計算一組值的最小值。
SUM - 計算一組值的和值。
LISTAGG - 連接一組字符串。
我們首先來看一個 AVG 函數(shù)的例子:
SELECT AVG(salary) AS "平均月薪"
FROM employee;
平均月薪|
-------|
9832|
以上查詢返回了全體員工的平均月薪。
關(guān)于聚合函數(shù),我們需要注意兩點:
在參數(shù)中使用DISTINCT選項可以在計算之前排除重復(fù)數(shù)據(jù);
聚合函數(shù)在計算時,忽略輸入為 NULL 值的行。
例如:
SELECT COUNT(*) AS "員工總數(shù)",
COUNT(DISTINCT sex) AS "不同性別",
COUNT(bonus) AS "擁有獎金"
FROM employee;
員工總數(shù)|不同性別|擁有獎金|
-------|-------|--------|
25| 2| 9|
其中,COUNT(*) 返回了所有的記錄數(shù)(員工總數(shù)),COUNT(DISTINCT sex) 返回了性別的種類,COUNT(bonus) 返回了擁有獎金的員工數(shù)量。
MIN 和 MAX 函數(shù)分別返回數(shù)據(jù)中的最小值和最大值,SUM 函數(shù)計算所有數(shù)據(jù)的和值。例如:
SELECT MIN(salary) AS "最低月薪",
MAX(salary) AS "最高月薪",
SUM(salary) AS "月薪總計"
FROM employee;
最低月薪|最高月薪 |月薪總計 |
-------|--------|----------|
4000| 30000| 245800|
LISTAGG 函數(shù)可以將一組字符串通過指定分隔符進行合并,例如:
SELECT LISTAGG(emp_name, ';') AS "員工列表"
FROM employee
WHERE dept_id = 1;
員工列表 |
-------------|
劉備;關(guān)羽;張飛|
LISTAGG 函數(shù)還提供了排序、去重、合并結(jié)果超長處理等功能,具體可以參考這篇文章。
??除了以上常見的聚合函數(shù)之外,Oracle 還支持例如方差函數(shù)、標(biāo)準差函數(shù)等更多的聚合函數(shù),需要時可以參考官方文檔。
8.2 數(shù)據(jù)分組
在前面的示例中,我們將所有的數(shù)據(jù)作為一個整體(分組),聚合函數(shù)只返回了一個結(jié)果。接下來,我們結(jié)合GROUP BY子句,將數(shù)據(jù)分成不同的組,然后分別計算各個組內(nèi)的數(shù)據(jù)匯總。
假如我們想要知道每個部門的平均薪水,而不是所有員工的平均值,可以使用以下語句:
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 函數(shù)計算每個部門的平均薪水。
如果GROUP BY分組字段存在 NULL 值,多個 NULL 值將被分為一個組。例如,以下語句按照不同獎金值統(tǒng)計員工的數(shù)量:
SELECT bonus AS "獎金",
COUNT(*) AS "員工數(shù)量"
FROM employee
GROUP BY bonus;
獎金 |員工數(shù)量|
-----|-------|
8000| 1|
1500| 1|
5000| 2|
| 16|
6000| 1|
10000| 3|
2000| 1|
從查詢結(jié)果可以看出,16 個員工沒有獎金;但他們都被分組同一個組中,而不是多個不同的組。
在使用分組匯總時,一個常見的錯誤就是SELECT列表中包含了既不是聚合函數(shù)、也不屬于GROUP BY子句的字段,例如:
-- 錯誤示例
SELECT dept_id,
emp_name,
AVG(salary)
FROM employee
GROUP BY dept_id;
SQL 錯誤 [979] [42000]: ORA-00979: 不是 GROUP BY 表達式
以上語句的錯誤在于 emp_name 既不是分組字段,也不是聚合函數(shù)。查詢按照部門進行分組,但是每個部門包含多個員工,數(shù)據(jù)庫無法知道需要顯示哪個員工的姓名。這是一個邏輯錯誤。
我們不僅可以基于一個字段進行分組,也可以依據(jù)多個字段將數(shù)據(jù)分成更多的組。例如,以下語句同時按照部門和性別統(tǒng)計員工的數(shù)量:
SELECT dept_id AS "部門編號",
sex AS "性別",
COUNT(*) AS "員工數(shù)量"
FROM employee
GROUP BY dept_id, sex;
部門編號|性別|員工數(shù)量|
-------|----|-------|
1|男 | 3|
2|男 | 3|
4|女 | 1|
4|男 | 8|
5|男 | 8|
3|女 | 2|
在 SQL 查詢中,如果同時存在WHERE子句和GROUP BY子句,要求WHERE子句出現(xiàn)在GROUP BY子句之前。因此,WHERE子句無法對分組后的結(jié)果進行過濾。例如,以下錯誤示例想要查找員工數(shù)量大于 5 人的部門:
-- 錯誤示例
SELECT dept_id,
COUNT(*)
FROM employee
WHERE COUNT(*) > 5
GROUP BY dept_id;
SQL 錯誤 [934] [42000]: ORA-00934: 此處不允許使用分組函數(shù)
錯誤原因在于WHERE子句執(zhí)行時還沒有進行分組計算,它只能基于分組之前的數(shù)據(jù)行進行過濾。如果需要對分組后的結(jié)果進行過濾,需要使用HAVING子句。
8.3 分組過濾
如果要查看員工數(shù)量大于 10 的部門編號,可以使用HAVING子句實現(xiàn):
SELECT dept_id,
COUNT(*) AS headcount
FROM employee
GROUP BY dept_id
HAVING COUNT(*) > 5;
DEPT_ID|HEADCOUNT|
-------|---------|
4| 9|
5| 8|
查詢首先按照部門進行分組,計算每個部門的員工數(shù)量;然后使用HAVING子句過濾員工數(shù)量大于 5 人的部門。
我們可以同時使用WHERE子句進行數(shù)據(jù)行的過濾,使用HAVING進行分組結(jié)果的過濾。例如想要查看薪水大于 10000 的員工數(shù)量大于 2 人的部門,可以使用以下查詢:
SELECT dept_id,
COUNT(*)
FROM employee
WHERE salary > 10000
GROUP BY dept_id
HAVING COUNT(*) > 2;
DEPT_ID|COUNT(*)|
-------|--------|
1| 3|
首先,WHERE子句過濾薪水大于 10000 的所有員工;然后按照部門進行分組,計算每個部門的員工數(shù)量;最后,使用HAVING子句過濾這類員工數(shù)量大于 2 人的部門。
最后,我們來看一下 SQL 標(biāo)準的語法:
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 的邏輯執(zhí)行順序為 FROM、WHERE、SELECT、GROUP BY、HAVING、ORDER BY 以及 OFFSET/FECTH。
Oracle 中的 GROUP BY 子句還支持一些高級分組選項,我們將會在第 13 篇中進行介紹。