學習重點
使用聚合函數對表中的列進行計算合計值或者平均值等的匯總操作。
通常,聚合函數會對
NULL以外的對象進行匯總。但是只有COUNT函數例外,使用COUNT(*)可以查出包含NULL在內的全部數據的行數。使用
DISTINCT關鍵字刪除重復值。
一、聚合函數
通過 SQL 對數據進行某種操作或計算時需要使用函數。例如,計算表中全部數據的行數時,可以使用 COUNT 函數。該函數就是使用 COUNT(計數)來命名的。除此之外,SQL 中還有很多其他用於匯總的函數,請大家先記住以下 5 個常用的函數。
KEYWORD
函數
COUNT函數
COUNT:計算表中的記錄數(行數)
SUM:計算表中數值列中數據的合計值
AVG:計算表中數值列中數據的平均值
MAX:求出表中任意列中數據的最大值
MIN:求出表中任意列中數據的最小值
如上所示,用於匯總的函數稱為聚合函數或者聚集函數,本教程中統稱為聚合函數。所謂聚合,就是將多行匯總為一行。實際上,所有的聚合函數都是這樣,輸入多行輸出一行。
KEYWORD
聚合函數
聚集函數
聚合
接下來,本文將繼續使用在 表的創建 中創建的 Product 表(圖 1)來學習函數的使用方法。
圖 1 Product 表的內容
二、計算表中數據的行數
首先,我們以 COUNT 函數為例讓大家對函數形成一個初步印象。函數這個詞,與我們在學校數學課上學到的意思是一樣的,就像是輸入某個值就能輸出相應結果的盒子一樣 [1]。
使用 COUNT 函數時,輸入表的列,就能夠輸出數據行數。如圖 2 所示,將表中的列放入名稱為 COUNT 的盒子中,咔嗒咔嗒地進行計算,咕咚一下行數就出來了……就像自動售貨機那樣,很容易理解吧。
圖 2 COUNT 函數的操作演示圖
接下來讓我們看一下 SQL 中的具體書寫方法。COUNT 函數的語法本身非常簡單,像代碼清單 1 那樣寫在 SELECT 子句中就可以得到表中全部數據的行數了。
代碼清單 1 計算全部數據的行數

執行結果

COUNT() 中的星號,我們在 SELECT 語句基礎 中已經學過,代表全部列的意思。COUNT 函數的輸入值就記述在其后的括號中。
此處的輸入值稱為參數或者 parameter,輸出值稱為返回值。這些稱謂不僅本教程中會使用,在多數編程語言中使用函數時都會頻繁出現,請大家牢記。
KEYWORD
參數(parameter)
返回值
三、計算 NULL 之外的數據的行數
想要計算表中全部數據的行數時,可以像 SELECT COUNT(*)~ 這樣使用星號。如果想得到 purchase_price 列(進貨單價)中非空行數的話,可以像代碼清單 2 那樣,通過將對象列設定為參數來實現。
代碼清單 2 計算 NULL 之外的數據行數
SELECT COUNT(purchase_price)
FROM Product;
執行結果
count
-------
6
此時,如圖 1 所示,purchase_price 列中有兩行數據是 NULL,因此並不應該計算這兩行。對於 COUNT 函數來說,參數列不同計算的結果也會發生變化,這一點請大家特別注意。為了有助於大家理解,請看如下這個只包含 NULL 的表的極端例子。
圖 3 只包含 NULL 的表
我們來看一下針對上述表,將星號(*)和列名作為參數傳遞給 COUNT 函數時所得到的結果(代碼清單 3)。
代碼清單 3 將包含 NULL 的列作為參數時,COUNT(*) 和 COUNT(<列名>) 的結果並不相同
SELECT COUNT(*), COUNT(col_1)
FROM NullTbl;
執行結果

如上所示,即使對同一個表使用 COUNT 函數,輸入的參數不同得到的結果也會不同。由於將列名作為參數時會得到 NULL 之外的數據行數,所以得到的結果是 0 行。
該特性是 COUNT 函數所特有的,其他函數並不能將星號作為參數(如果使用星號會出錯)。
法則 1
COUNT函數的結果根據參數的不同而不同。COUNT(*)會得到包含NULL的數據行數,而COUNT(<列名>)會得到NULL之外的數據行數。
四、計算合計值
接下來我們學習其他 4 個聚合函數的使用方法。這些函數的語法基本上與 COUNT 函數相同,但就像我們此前所說的那樣,在這些函數中不能使用星號作為參數。
首先,我們使用計算合計值的 SUM 函數,求出銷售單價的合計值(代碼清單 4)。
KEYWORD
SUM函數
代碼清單 4 計算銷售單價的合計值
SELECT SUM(sale_price)
FROM Product;
執行結果
sum
------
16780
得到的結果 16780 日元,是所有銷售單價(sale_price 列)的合計,與下述計算式的結果相同。

接下來,我們將銷售單價和進貨單價(purchase_price 列)的合計值一起計算出來(代碼清單 5)。
代碼清單 5 計算銷售單價和進貨單價的合計值
SELECT SUM(sale_price), SUM(purchase_price)
FROM Product;
執行結果

這次我們通過 SUM(purchase_price) 將進貨單價的合計值也一起計算出來了,但有一點需要大家注意。具體的計算過程如下所示。

大家都已經注意到了吧,與銷售單價不同,進貨單價中有兩條不明數據 NULL。對於 SUM 函數來說,即使包含 NULL,也可以計算出合計值。還記得 算術運算符和比較運算符 中內容的讀者可能會產生如下疑問。
“四則運算中如果存在 NULL,結果一定是 NULL,那此時進貨單價的合計值會不會也是 NULL 呢?”
有這樣疑問的讀者思維很敏銳,但實際上這兩者並不矛盾。從結果上說,所有的聚合函數,如果以列名為參數,那么在計算之前就已經把 NULL 排除在外了。因此,無論有多少個 NULL 都會被無視。這與“等價為 0”並不相同 [2]。
因此,上述進貨單價的計算表達式,實際上應該如下所示。

法則 2
聚合函數會將
NULL排除在外。但COUNT(*)例外,並不會排除NULL。
五、計算平均值
接下來,我們練習一下計算多行數據的平均值。為此,我們需要使用 AVG 函數,其語法和 SUM 函數完全相同(代碼清單 6)。
KEYWORD
AVG函數
代碼清單 6 計算銷售單價的平均值
SELECT AVG(sale_price)
FROM Product;
執行結果
avg
----------------------
2097.5000000000000000
平均值的計算式如下所示。

( 值的合計 )/( 值的個數 ) 就是平均值的計算公式了。下面我們也像使用 SUM 函數那樣,計算一下包含 NULL 的進貨單價的平均值(代碼清單 7)。
代碼清單 7 計算銷售單價和進貨單價的平均值
SELECT AVG(sale_price), AVG(purchase_price)
FROM Product;
執行結果

計算進貨單價平均值的情況與 SUM 函數相同,會事先刪除 NULL 再進行計算,因此計算式如下所示。

需要注意的是分母是 6 而不是 8,減少的兩個也就是那兩條 NULL 的數據。
但是有時也想將 NULL 作為 0 進行計算。
但是有時也想將 NULL 作為 0 進行計算,具體的實現方式請參 各種各樣的函數。

六、計算最大值和最小值
想要計算出多條記錄中的最大值或最小值,可以分別使用 MAX 和 MIN 函數,它們是英語 maximam(最大值)和 minimum(最小值)的縮寫,很容易記住。
KEYWORD
MAX函數
MIN函數
這兩個函數的語法與 SUM 的語法相同,使用時需要將列作為參數(代碼清單 8)。
代碼清單 8 計算銷售單價的最大值和進貨單價的最小值
SELECT MAX(sale_price), MIN(purchase_price)
FROM Product;
執行結果

如圖 3 所示,我們取得了相應的最大值和最小值。
但是,MAX/MIN 函數和 SUM/AVG 函數有一點不同,那就是 SUM/AVG 函數只能對數值類型的列使用,而 MAX/MIN 函數原則上可以適用於任何數據類型的列。例如,對圖 1 中日期類型的列 regist_date 使用 MAX/MIN 函數進行計算的結果如下所示(代碼清單 9)。
代碼清單 9 計算登記日期的最大值和最小值
SELECT MAX(regist_date), MIN(regist_date)
FROM Product;
執行結果

剛剛我們說過 MAX/MIN 函數適用於任何數據類型的列,也就是說,只要是能夠排序的數據,就肯定有最大值和最小值,也就能夠使用這兩個函數。對日期來說,平均值和合計值並沒有什么實際意義,因此不能使用 SUM/AVG 函數。這點對於字符串類型的數據也適用,字符串類型的數據能夠使用 MAX/MIN 函數,但不能使用 SUM/AVG 函數。
法則 3
MAX/MIN函數幾乎適用於所有數據類型的列。SUM/AVG函數只適用於數值類型的列。
七、使用聚合函數刪除重復值(關鍵字 DISTINCT)
接下來我們考慮一下下面這種情況。
在圖 1 中我們可以看到,商品種類(product_type 列)和銷售單價(sale_price 列)的數據中,存在多行數據相同的情況。
例如,拿商品種類來說,表中總共有 3 種商品共 8 行數據,其中衣服 2 行,辦公用品 2 行,廚房用具 4 行。如果想要計算出商品種類的個數,怎么做比較好呢?刪除重復數據然后再計算數據行數似乎是個不錯的辦法。實際上,在使用 COUNT 函數時,將 SELECT 語句基礎 中介紹過的 DISTINCT 關鍵字作為參數,就能得到我們想要的結果了(代碼清單 10)。
KEYWORD
DISTINCT關鍵字
代碼清單 10 計算去除重復數據后的數據行數
SELECT COUNT(DISTINCT product_type)
FROM Product;
執行結果
count
-------
3
請注意,這時 DISTINCT 必須寫在括號中。這是因為必須要在計算行數之前刪除 product_type 列中的重復數據。如果像代碼清單 11 那樣寫在括號外的話,就會先計算出數據行數,然后再刪除重復數據,結果就得到了 product_type 列的所有行數(也就是 8)。
代碼清單 11 先計算數據行數再刪除重復數據的結果
SELECT DISTINCT COUNT(product_type)
FROM Product;
執行結果
count
-------
8
法則 4
想要計算值的種類時,可以在
COUNT函數的參數中使用DISTINCT。
不僅限於 COUNT 函數,所有的聚合函數都可以使用 DISTINCT。下面我們來看一下使用 DISTINCT 和不使用 DISTINCT 時 SUM 函數的執行結果(代碼清單 12)。
代碼清單 12 使不使用 DISTINCT 時的動作差異(SUM 函數)
SELECT SUM(sale_price), SUM(DISTINCT sale_price)
FROM Product;
執行結果

左側是未使用 DISTINCT 時的合計值,和我們之前計算的結果相同,都是 16780 日元。右側是使用 DISTINCT 后的合計值,比之前的結果少了 500 日元。這是因為表中銷售單價為 500 日元的商品有兩種——“打孔器”和“叉子”,在刪除重復數據之后,計算對象就只剩下一條記錄了。
法則 5
在聚合函數的參數中使用
DISTINCT,可以刪除重復數據。
請參閱
(完)
