SQL 對表進行聚合查詢


學習重點

  • 使用聚合函數對表中的列進行計算合計值或者平均值等的匯總操作。

  • 通常,聚合函數會對 NULL 以外的對象進行匯總。但是只有 COUNT 函數例外,使用 COUNT(*) 可以查出包含 NULL 在內的全部數據的行數。

  • 使用 DISTINCT 關鍵字刪除重復值。

一、聚合函數

通過 SQL 對數據進行某種操作或計算時需要使用函數。例如,計算表中全部數據的行數時,可以使用 COUNT 函數。該函數就是使用 COUNT(計數)來命名的。除此之外,SQL 中還有很多其他用於匯總的函數,請大家先記住以下 5 個常用的函數。

KEYWORD

  • 函數

  • COUNT 函數

COUNT:計算表中的記錄數(行數)

SUM:計算表中數值列中數據的合計值

AVG:計算表中數值列中數據的平均值

MAX:求出表中任意列中數據的最大值

MIN:求出表中任意列中數據的最小值

如上所示,用於匯總的函數稱為聚合函數或者聚集函數,本教程中統稱為聚合函數。所謂聚合,就是將多行匯總為一行。實際上,所有的聚合函數都是這樣,輸入多行輸出一行。

KEYWORD

  • 聚合函數

  • 聚集函數

  • 聚合

接下來,本文將繼續使用在 表的創建 中創建的 Product 表(圖 1)來學習函數的使用方法。

Product 表的內容

圖 1 Product 表的內容

二、計算表中數據的行數

首先,我們以 COUNT 函數為例讓大家對函數形成一個初步印象。函數這個詞,與我們在學校數學課上學到的意思是一樣的,就像是輸入某個值就能輸出相應結果的盒子一樣 [1]

使用 COUNT 函數時,輸入表的列,就能夠輸出數據行數。如圖 2 所示,將表中的列放入名稱為 COUNT 的盒子中,咔嗒咔嗒地進行計算,咕咚一下行數就出來了……就像自動售貨機那樣,很容易理解吧。

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 的表的極端例子。

只包含 NULL 的表

圖 3 只包含 NULL 的表

我們來看一下針對上述表,將星號(*)和列名作為參數傳遞給 COUNT 函數時所得到的結果(代碼清單 3)。

代碼清單 3 將包含 NULL 的列作為參數時,COUNT(*)COUNT(<列名>) 的結果並不相同

SELECT COUNT(*), COUNT(col_1)
  FROM NullTbl;

執行結果

將包含 NULL 的列作為參數時,COUNT(*) 和 COUNT(<列名>) 的結果並不相同

如上所示,即使對同一個表使用 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 再進行計算,因此計算式如下所示。

計算進貨單價平均值的情況與 SUM 函數相同,會事先刪除 NULL 再進行計算

需要注意的是分母是 6 而不是 8,減少的兩個也就是那兩條 NULL 的數據。

但是有時也想將 NULL 作為 0 進行計算。
但是有時也想將 NULL 作為 0 進行計算,具體的實現方式請參 各種各樣的函數

將 NULL 作為 0 進行計算

六、計算最大值和最小值

想要計算出多條記錄中的最大值或最小值,可以分別使用 MAXMIN 函數,它們是英語 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 和不使用 DISTINCTSUM 函數的執行結果(代碼清單 12)。

代碼清單 12 使不使用 DISTINCT 時的動作差異(SUM 函數)

SELECT SUM(sale_price), SUM(DISTINCT sale_price)
  FROM Product;

執行結果

使不使用 DISTINCT 時的動作差異

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

法則 5

在聚合函數的參數中使用 DISTINCT,可以刪除重復數據。

請參閱

(完)


  1. 函數中的函就是盒子的意思。 ↩︎

  2. 雖然使用 SUM 函數時,“將 NULL 除外”和“等同於 0”的結果相同,但使用 AVG 函數時,這兩種情況的結果就完全不同了。接下來我們會詳細介紹在 AVG 函數中使用包含 NULL 的列作為參數的例子。 ↩︎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM