1. 程式人生 > 其它 >MySQL學習筆記:八、聚合函式

MySQL學習筆記:八、聚合函式

我們上一章講到了 SQL 單行函式。實際上 SQL 函式還有一類,叫做聚合(或聚集、分組)函式,它是對一組資料進行彙總的函式,輸入的是一組資料的集合,輸出的是單個值。

1. 聚合函式介紹

  • 什麼是聚合函式

聚合函式作用於一組資料,並對一組資料返回一個值。


  • 聚合函式型別

    • AVG()
    • SUM()
    • MAX()
    • MIN()
    • **COUNT() **
  • 聚合函式語法


  • 聚合函式不能巢狀呼叫。比如不能出現類似“AVG(SUM(欄位名稱))”形式的呼叫。

1.1 AVG和SUM函式

可以對數值型資料使用AVG 和 SUM 函式。

SELECT AVG(salary), MAX(salary),MIN(salary), SUM(salary)
FROM   employees
WHERE  job_id LIKE '%REP%';


1.2 MIN和MAX函式

可以對任意資料型別的資料使用 MIN 和 MAX 函式。

SELECT MIN(hire_date), MAX(hire_date)
FROM	  employees;


1.3 COUNT函式

  • COUNT(*)返回表中記錄總數,適用於任意資料型別
SELECT COUNT(*)
FROM	  employees
WHERE  department_id = 50;


  • COUNT(expr) 返回expr不為空的記錄總數。
SELECT COUNT(commission_pct)
FROM   employees
WHERE  department_id = 50;


  • 問題:用count(*),count(1),count(列名)誰好呢?

    其實,對於MyISAM引擎的表是沒有區別的。這種引擎內部有一計數器在維護著行數。

    Innodb引擎的表用count(*),count(1)直接讀行數,複雜度是O(n),因為innodb真的要去數一遍。但好於具體的count(列名)。

  • 問題:能不能使用count(列名)替換count(*)?

    不要使用 count(列名)來替代 count(*)count(*)是 SQL92 定義的標準統計行數的語法,跟資料庫無關,跟 NULL 和非 NULL 無關。

    說明:count(*)會統計值為 NULL 的行,而 count(列名)不會統計此列為 NULL 值的行。

2. GROUP BY

2.1 基本使用


可以使用GROUP BY子句將表中的資料分成若干組

SELECT column, group_function(column)
FROM table
[WHERE	condition]
[GROUP BY	group_by_expression]
[ORDER BY	column];

明確:WHERE一定放在FROM後面

在SELECT列表中所有未包含在組函式中的列都應該包含在 GROUP BY子句中

SELECT   department_id, AVG(salary)
FROM     employees
GROUP BY department_id ;



包含在 GROUP BY 子句中的列不必包含在SELECT 列表中

SELECT   AVG(salary)
FROM     employees
GROUP BY department_id ;


2.2 使用多個列分組


SELECT   department_id dept_id, job_id, SUM(salary)
FROM     employees
GROUP BY department_id, job_id ;



2.3 GROUP BY中使用WITH ROLLUP

使用WITH ROLLUP關鍵字之後,在所有查詢出的分組記錄之後增加一條記錄,該記錄計算查詢出的所有記錄的總和,即統計記錄數量。

SELECT department_id,AVG(salary)
FROM employees
WHERE department_id > 80
GROUP BY department_id WITH ROLLUP;

注意:

當使用ROLLUP時,不能同時使用ORDER BY子句進行結果排序,即ROLLUP和ORDER BY是互相排斥的。

3. HAVING

3.1 基本使用


過濾分組:HAVING子句

  1. 行已經被分組。
  2. 使用了聚合函式。
  3. 滿足HAVING 子句中條件的分組將被顯示。
  4. HAVING 不能單獨使用,必須要跟 GROUP BY 一起使用。


SELECT   department_id, MAX(salary)
FROM     employees
GROUP BY department_id
HAVING   MAX(salary)>10000 ;


  • **非法使用聚合函式 : 不能在 WHERE 子句中使用聚合函式。**如下:
SELECT   department_id, AVG(salary)
FROM     employees
WHERE    AVG(salary) > 8000
GROUP BY department_id;


3.2 WHERE和HAVING的對比

區別1:WHERE 可以直接使用表中的欄位作為篩選條件,但不能使用分組中的計算函式作為篩選條件;HAVING 必須要與 GROUP BY 配合使用,可以把分組計算的函式和分組欄位作為篩選條件。

這決定了,在需要對資料進行分組統計的時候,HAVING 可以完成 WHERE 不能完成的任務。這是因為,在查詢語法結構中,WHERE 在 GROUP BY 之前,所以無法對分組結果進行篩選。HAVING 在 GROUP BY 之後,可以使用分組欄位和分組中的計算函式,對分組的結果集進行篩選,這個功能是 WHERE 無法完成的。另外,WHERE排除的記錄不再包括在分組中。

區別2:如果需要通過連線從關聯表中獲取需要的資料,WHERE 是先篩選後連線,而 HAVING 是先連線後篩選。 這一點,就決定了在關聯查詢中,WHERE 比 HAVING 更高效。因為 WHERE 可以先篩選,用一個篩選後的較小資料集和關聯表進行連線,這樣佔用的資源比較少,執行效率也比較高。HAVING 則需要先把結果集準備好,也就是用未被篩選的資料集進行關聯,然後對這個大的資料集進行篩選,這樣佔用的資源就比較多,執行效率也較低。

小結如下:

優點 缺點
WHERE 先篩選資料再關聯,執行效率高 不能使用分組中的計算函式進行篩選
HAVING 可以使用分組中的計算函式 在最後的結果集中進行篩選,執行效率較低

開發中的選擇:

WHERE 和 HAVING 也不是互相排斥的,我們可以在一個查詢裡面同時使用 WHERE 和 HAVING。包含分組統計函式的條件用 HAVING,普通條件用 WHERE。這樣,我們就既利用了 WHERE 條件的高效快速,又發揮了 HAVING 可以使用包含分組統計函式的查詢條件的優點。當資料量特別大的時候,執行效率會有很大的差別。

4. SELECT的執行過程

4.1 查詢的結構

#方式1:
SELECT ...,....,...
FROM ...,...,....
WHERE 多表的連線條件
AND 不包含組函式的過濾條件
GROUP BY ...,...
HAVING 包含組函式的過濾條件
ORDER BY ... ASC/DESC
LIMIT ...,...

#方式2:
SELECT ...,....,...
FROM ... JOIN ... 
ON 多表的連線條件
JOIN ...
ON ...
WHERE 不包含組函式的過濾條件
AND/OR 不包含組函式的過濾條件
GROUP BY ...,...
HAVING 包含組函式的過濾條件
ORDER BY ... ASC/DESC
LIMIT ...,...

#其中:
#(1)from:從哪些表中篩選
#(2)on:關聯多表查詢時,去除笛卡爾積
#(3)where:從表中篩選的條件
#(4)group by:分組依據
#(5)having:在統計結果中再次篩選
#(6)order by:排序
#(7)limit:分頁

4.2 SELECT執行順序

你需要記住 SELECT 查詢時的兩個順序:

1. 關鍵字的順序是不能顛倒的:

SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT...

2.SELECT 語句的執行順序(在 MySQL 和 Oracle 中,SELECT 執行順序基本相同):

FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的欄位 -> DISTINCT -> ORDER BY -> LIMIT


比如你寫了一個 SQL 語句,那麼它的關鍵字順序和執行順序是下面這樣的:

SELECT DISTINCT player_id, player_name, count(*) as num   順序 5
FROM player JOIN team ON player.team_id = team.team_id   順序 1
WHERE height > 1.80   順序 2
GROUP BY player.team_id   順序 3
HAVING num > 2   順序 4
ORDER BY num DESC   順序 6
LIMIT 2   順序 7

在 SELECT 語句執行這些步驟的時候,每個步驟都會產生一個虛擬表,然後將這個虛擬表傳入下一個步驟中作為輸入。需要注意的是,這些步驟隱含在 SQL 的執行過程中,對於我們來說是不可見的。

4.3 SQL 的執行原理

SELECT 是先執行 FROM 這一步的。在這個階段,如果是多張表聯查,還會經歷下面的幾個步驟:

  1. 首先先通過 CROSS JOIN 求笛卡爾積,相當於得到虛擬表 vt(virtual table)1-1;
  2. 通過 ON 進行篩選,在虛擬表 vt1-1 的基礎上進行篩選,得到虛擬表 vt1-2;
  3. 新增外部行。如果我們使用的是左連線、右連結或者全連線,就會涉及到外部行,也就是在虛擬表 vt1-2 的基礎上增加外部行,得到虛擬表 vt1-3。

當然如果我們操作的是兩張以上的表,還會重複上面的步驟,直到所有表都被處理完為止。這個過程得到是我們的原始資料。

當我們拿到了查詢資料表的原始資料,也就是最終的虛擬表 vt1,就可以在此基礎上再進行 WHERE 階段。在這個階段中,會根據 vt1 表的結果進行篩選過濾,得到虛擬表 vt2

然後進入第三步和第四步,也就是 GROUP 和 HAVING 階段。在這個階段中,實際上是在虛擬表 vt2 的基礎上進行分組和分組過濾,得到中間的虛擬表 vt3vt4

當我們完成了條件篩選部分之後,就可以篩選表中提取的欄位,也就是進入到 SELECT 和 DISTINCT 階段

首先在 SELECT 階段會提取想要的欄位,然後在 DISTINCT 階段過濾掉重複的行,分別得到中間的虛擬表 vt5-1vt5-2

當我們提取了想要的欄位資料之後,就可以按照指定的欄位進行排序,也就是 ORDER BY 階段,得到虛擬表 vt6

最後在 vt6 的基礎上,取出指定行的記錄,也就是 LIMIT 階段,得到最終的結果,對應的是虛擬表 vt7

當然我們在寫 SELECT 語句的時候,不一定存在所有的關鍵字,相應的階段就會省略。

同時因為 SQL 是一門類似英語的結構化查詢語言,所以我們在寫 SELECT 語句的時候,還要注意相應的關鍵字順序,所謂底層執行的原理,就是我們剛才講到的執行順序。