1. 程式人生 > 其它 >SQL 入門 -- 聚集函式(四)

SQL 入門 -- 聚集函式(四)

聚集函式,它是對一組資料進行彙總的函式,輸入的是一組資料的集合,輸出的是單個值。

通常我們可以利用聚集函式彙總表的資料,如果稍微複雜一些,我們還需要先對資料做篩選,然後再進行聚集,比如先按照某個條件進行分組,對分組條件進行篩選,然後得到篩選後的分組的彙總資訊。

重點內容:
  1. 聚集函式都有哪些,能否在一條 SELECT 語句中使用多個聚集函式;
  2. 如何對資料進行分組,並進行聚集統計;
  3. 如何使用 HAVING 過濾分組,HAVING 和 WHERE 的區別是什麼。

聚集函式都有哪些:

SQL 中的聚集函式一共包括 5 個,可以幫我們求某列的最大值、最小值和平均值等,它們分別是:

繼續使用 heros 資料表,對王者榮耀的英雄資料進行聚合。如果我們想要查詢最大生命值大於 6000 的英雄數量。
SQL:SELECT COUNT(*) FROM heros WHERE hp_max > 6000

想要查詢最大生命值大於 6000,且有次要定位的英雄數量,需要使用 COUNT 函式。
SQL:SELECT COUNT(role_assist) FROM heros WHERE hp_max > 6000

需要說明的是,有些英雄沒有次要定位,即 role_assist 為 NULL,這時COUNT(role_assist)會忽略值為 NULL 的資料行,而 COUNT(*) 只是統計資料行數,不管某個欄位是否為 NULL。

如果我們想要查詢射手(主要定位或者次要定位是射手)的最大生命值的最大值是多少,需要使用 MAX 函式。
SQL:SELECT MAX(hp_max) FROM heros WHERE role_main = '射手' or role_assist = '射手'

如果想要知道英雄的數量,我們使用的是 COUNT(*) 函式,求平均值、最大值、最小值,以及總的防禦最大值,我們分別使用的是 AVG、MAX、MIN 和 SUM 函式。另外我們還需要對英雄的主要定位和次要定位進行篩選,使用的是WHERE role_main = '射手' or role_assist = '射手'。

SQL: SELECT COUNT(*), AVG(hp_max), MAX(mp_max), MIN(attack_max), SUM(defense_max) FROM heros WHERE role_main = '射手' or role_assist = '射手'

需要說明的是 AVG、MAX、MIN 等聚集函式會自動忽略值為 NULL 的資料行,MAX 和 MIN 函式也可以用於字串型別資料的統計,如果是英文字母,則按照 A—Z 的順序排列,越往後,數值越大。如果是漢字則按照全拼拼音進行排列。比如:
SQL:SELECT MIN(CONVERT(name USING gbk)), MAX(CONVERT(name USING gbk)) FROM heros;

需要說明的是,我們需要先把 name 欄位統一轉化為 gbk 型別,使用CONVERT(name USING gbk),然後再使用 MIN 和 MAX 取最小值和最大值。

也可以對資料行中不同的取值進行聚集,先用 DISTINCT 函式取不同的資料,然後再使用聚集函式。比如我們想要查詢不同的生命最大值的英雄數量是多少。

SQL: SELECT COUNT(DISTINCT hp_max) FROM heros
執行結果為 61。

在 heros 這個資料表中,一共有 69 個英雄數量,生命最大值不一樣的英雄數量是 61 個。

我們想要統計不同生命最大值英雄的平均生命最大值,保留小數點後兩位。首先需要取不同生命最大值,即DISTINCT hp_max,然後針對它們取平均值,即AVG(DISTINCT hp_max),最後再針對這個值保留小數點兩位,也就是ROUND(AVG(DISTINCT hp_max), 2)。

SQL: SELECT ROUND(AVG(DISTINCT hp_max), 2) FROM heros

如果我們不使用 DISTINCT 函式,就是對全部資料進行聚集統計。如果使用了 DISTINCT 函式,就可以對數值不同的資料進行聚集。

一般我們使用 MAX 和 MIN 函式統計資料行的時候,不需要再額外使用 DISTINCT,因為使用 DISTINCT 和全部資料行進行最大值、最小值的統計結果是相等的。

如何對資料進行分組,並進行聚集統計:

做統計的時候,可能需要先對資料按照不同的數值進行分組,然後對這些分好的組進行聚集統計。對資料進行分組,需要使用 GROUP BY 子句。

按照英雄的主要定位進行分組,並統計每組的英雄數量。
SQL: SELECT COUNT(*), role_main FROM heros GROUP BY role_main

對英雄按照次要定位進行分組,並統計每組英雄的數量。
SELECT COUNT(*), role_assist FROM heros GROUP BY role_assist


如果欄位為 NULL,也會被列為一個分組。在這個查詢統計中,次要定位為 NULL,即只有一個主要定位的英雄是 40 個。

也可以使用多個欄位進行分組,這就相當於把這些欄位可能出現的所有的取值情況都進行分組。比如,我們想要按照英雄的主要定位、次要定位進行分組,檢視這些英雄的數量,並按照這些分組的英雄數量從高到低進行排序。
SELECT COUNT(*) as num, role_main, role_assist FROM heros GROUP BY role_main, role_assist ORDER BY num DESC

HAVING 過濾分組,它與 WHERE 的區別是什麼?

HAVING 的作用和 WHERE 一樣,都是起到過濾的作用,只不過 WHERE 是用於資料行,而 HAVING 則作用於分組。

按照英雄的主要定位、次要定位進行分組,並且篩選分組中英雄數量大於 5 的組,最後按照分組中的英雄數量從高到低進行排序。
1、首先需要獲取的是英雄的數量、主要定位和次要定位
即SELECT COUNT(*) as num, role_main, role_assist。
2、然後按照英雄的主要定位和次要定位進行分組
即GROUP BY role_main, role_assist
3、對分組中的英雄數量進行篩選,選擇大於 5 的分組
即HAVING num > 5
4、然後按照英雄數量從高到低進行排序
即ORDER BY num DESC
SQL: SELECT COUNT(*) as num, role_main, role_assist FROM heros GROUP BY role_main, role_assist HAVING num > 5 ORDER BY num DESC

還是上面這個分組,只不過我們按照數量進行了過濾,篩選了數量大於 5 的分組進行輸出。如果把 HAVING 替換成了 WHERE,SQL 則會報錯。對於分組的篩選,我們一定要用 HAVING,而不是 WHERE。

HAVING 支援所有 WHERE 的操作,因此所有需要 WHERE 子句實現的功能,你都可以使用 HAVING 對分組進行篩選。

WHERE 和 HAVING 進行條件過濾的區別

篩選最大生命值大於 6000 的英雄,按照主要定位、次要定位進行分組,並且顯示分組中英雄數量大於 5 的分組,按照數量從高到低進行排序。

SQL: SELECT COUNT(*) as num, role_main, role_assist FROM heros WHERE hp_max > 6000 GROUP BY role_main, role_assist HAVING num > 5 ORDER BY num DESC

還是針對上一個例子的查詢,只是我們先增加了一個過濾條件,即篩選最大生命值大於 6000 的英雄。這裡我們就需要先使用 WHERE 子句對最大生命值大於 6000 的英雄進行條件過濾,然後再使用 GROUP BY 進行分組,使用 HAVING 進行分組的條件判斷,然後使用 ORDER BY 進行排序。

總結

通常我們會對資料先進行分組,然後再使用聚集函式統計不同組的資料概況,比如資料行數、平均值、最大值、最小值以及求和等。
我們也可以使用 HAVING 對分組進行過濾,然後通過 ORDER BY 按照某個欄位的順序進行排序輸出。有時候你能看到在一條 SELECT 語句中,可能會包括多個子句,用 WHERE 進行資料量的過濾,用 GROUP BY 進行分組,用 HAVING 進行分組過濾,用 ORDER BY 進行排序……

你要記住,在 SELECT 查詢中,關鍵字的順序是不能顛倒的,它們的順序是:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...

另外需要注意的是,使用 GROUP BY 進行分組,如果想讓輸出的結果有序,可以在 GROUP BY 後使用 ORDER BY。因為 GROUP BY 只起到了分組的作用,排序還是需要通過 ORDER BY 來完成。