1. 程式人生 > 其它 >如何使用 SQL GROUP BY 分組和排序資料

如何使用 SQL GROUP BY 分組和排序資料

目錄

本文介紹如何使用 SQL GROUP BY 子句分組資料,以便彙總表內容的子集。這涉及兩個新 SELECT 語句子句:GROUP BY 子句和 HAVING 子句。

一、資料分組

如何使用 SQL AVG、COUNT、MAX、MIN 和 SUM 彙總資料 中得知,使用 SQL 聚集函式可以彙總資料。這樣,我們就能夠對行進行計數,計算和與平均數,不檢索所有資料就獲得最大值和最小值。

目前為止的所有計算都是在表的所有資料或匹配特定的 WHERE 子句的資料上進行的。比如下面的例子返回供應商 DLL01

提供的產品數目:

SELECT COUNT(*) AS num_prods
FROM Products
WHERE vend_id = 'DLL01';

輸出:

num_prods
-----------
4

如果要返回每個供應商提供的產品數目,該怎麼辦?或者返回只提供一項產品的供應商的產品,或者返回提供 10 個以上產品的供應商的產品,怎麼辦?

這就是分組大顯身手的時候了。使用分組可以將資料分為多個邏輯組,對每個組進行聚集計算。

二、建立分組

分組是使用 SELECT 語句的 GROUP BY 子句建立的。理解分組的最好辦法是看一個例子:

SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;

輸出:

vend_id     num_prods
-------     ---------
BRS01       3
DLL01       4
FNG01       2

上面的 SELECT 語句指定了兩個列:vend_id 包含產品供應商的 IDnum_prods 為計算欄位(用 COUNT(*) 函式建立)。

GROUP BY 子句指示 DBMS 按 vend_id 排序並分組資料。這就會對每個 vend_id 而不是整個表計算 num_prods 一次。

從輸出中可以看到,供應商 BRS01 有 3 個產品,供應商 DLL01 有 4 個產品,而供應商 FNG01 有 2 個產品。

因為使用了 GROUP BY

,就不必指定要計算和估值的每個組了。系統會自動完成。GROUP BY 子句指示 DBMS 分組資料,然後對每個組而不是整個結果集進行聚集。

在使用 GROUP BY 子句前,需要知道一些重要的規定。

  • GROUP BY 子句可以包含任意數目的列,因而可以對分組進行巢狀,更細緻地進行資料分組。
  • 如果在 GROUP BY 子句中嵌套了分組,資料將在最後指定的分組上進行彙總。換句話說,在建立分組時,指定的所有列都一起計算(所以不能從個別的列取回資料)。
  • GROUP BY 子句中列出的每一列都必須是檢索列或有效的表示式(但不能是聚集函式)。如果在 SELECT 中使用表示式,則必須在 GROUP BY 子句中指定相同的表示式。不能使用別名。
  • 大多數 SQL 實現不允許 GROUP BY 列帶有長度可變的資料型別(如文字或備註型欄位)。
  • 除聚集計算語句外,SELECT 語句中的每一列都必須在 GROUP BY 子句中給出。
  • 如果分組列中包含具有 NULL 值的行,則 NULL 將作為一個分組返回。如果列中有多行 NULL 值,它們將分為一組。
  • GROUP BY 子句必須出現在 WHERE 子句之後,ORDER BY 子句之前。

提示:ALL 子句

Microsoft SQL Server 等有些 SQL 實現在 GROUP BY 中支援可選的 ALL 子句。這個子句可用來返回所有分組,即使是沒有匹配行的分組也返回(在此情況下,聚集將返回 NULL)。

具體的 DBMS 是否支援 ALL,請參閱相應的文件。

注意:通過相對位置指定列

有的 SQL 實現允許根據 SELECT 列表中的位置指定 GROUP BY 的列。例如,GROUP BY 2, 1 可表示按選擇的第二個列分組,然後再按第一個列分組。

雖然這種速記語法很方便,但並非所有 SQL 實現都支援,並且使用它容易在編輯 SQL 語句時出錯。

三、過濾分組

除了能用 GROUP BY 分組資料外,SQL 還允許過濾分組,規定包括哪些分組,排除哪些分組。例如,你可能想要列出至少有兩個訂單的所有顧客。為此,必須基於完整的分組而不是個別的行進行過濾。

我們已經看到了 WHERE 子句的作用(如何使用 SQL WHERE 過濾返回的資料 提及)。

但是,在這個例子中 WHERE 不能完成任務,因為 WHERE 過濾指定的是行而不是分組。事實上,WHERE 沒有分組的概念。

那麼,不使用 WHERE 使用什麼呢?SQL 為此提供了另一個子句,就是 HAVING 子句。HAVING 非常類似於 WHERE

事實上,目前為止所學過的所有型別的 WHERE 子句都可以用 HAVING 來替代。唯一的差別是,WHERE 過濾行,而 HAVING 過濾分組。

提示:HAVING 支援所有 WHERE 操作符

如何使用 SQL WHERE 過濾返回的資料如何使用 SQL AND、OR、IN 和 NOT 過濾返回的資料 中,我們學習了 WHERE 子句的條件(包括萬用字元條件和帶多個操作符的子句)。

學過的這些有關 WHERE 的所有技術和選項都適用於 HAVING。它們的句法是相同的,只是關鍵字有差別。

那麼,怎麼過濾分組呢?請看以下的例子:

SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;

輸出:

cust_id        orders
----------     -----------
1000000001     2

這條 SELECT 語句的前三行類似於上面的語句。最後一行增加了 HAVING 子句,它過濾 COUNT(*) >= 2(兩個以上訂單)的那些分組。

可以看到,WHERE 子句在這裡不起作用,因為過濾是基於分組聚集值,而不是特定行的值。

說明:HAVINGWHERE 的差別

這裡有另一種理解方法,WHERE 在資料分組前進行過濾,HAVING 在資料分組後進行過濾。

這是一個重要的區別,WHERE 排除的行不包括在分組中。這可能會改變計算值,從而影響 HAVING 子句中基於這些值過濾掉的分組。

那麼,有沒有在一條語句中同時使用 WHEREHAVING 子句的需要呢?

事實上,確實有。假如想進一步過濾上面的語句,使它返回過去 12 個月內具有兩個以上訂單的顧客。

為此,可增加一條 WHERE 子句,過濾出過去 12 個月內下過的訂單,然後再增加 HAVING 子句過濾出具有兩個以上訂單的分組。

為了更好地理解,來看下面的例子,它列出具有兩個以上產品且其價格大於等於 4 的供應商:

SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;

輸出:

vend_id     num_prods
-------     -----------
BRS01       3
FNG01       2

這條語句中,第一行是使用了聚集函式的基本 SELECT 語句,很像前面的例子。

WHERE 子句過濾所有 prod_price 至少為 4 的行,然後按 vend_id 分組資料,HAVING 子句過濾計數為 22 以上的分組。

如果沒有 WHERE 子句,就會多檢索出一行(供應商 DLL01,銷售 4 個產品,價格都在 4 以下):

SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id
HAVING COUNT(*) >= 2;

輸出:

vend_id     num_prods
-------     -----------
BRS01       3
DLL01       4
FNG01       2

說明:使用 HAVINGWHERE

HAVINGWHERE 非常類似,如果不指定 GROUP BY,則大多數 DBMS 會同等對待它們。

不過,你自己要能區分這一點。使用 HAVING 時應該結合 GROUP BY 子句,而 WHERE 子句用於標準的行級過濾。

四、分組和排序

GROUP BYORDER BY 經常完成相同的工作,但它們非常不同,理解這一點很重要。表 1 彙總了它們之間的差別。

表 1 ORDER BYGROUP BY

ORDER BY GROUP BY
對產生的輸出排序 對行分組,但輸出可能不是分組的順序
任意列都可以使用(甚至非選擇的列也可以使用) 只可能使用選擇列或表示式列,而且必須使用每個選擇列表達式
不一定需要 如果與聚集函式一起使用列(或表示式),則必須使用

表 1 中列出的第一項差別極為重要。我們經常發現,用 GROUP BY 分組的資料確實是以分組順序輸出的。但並不總是這樣,這不是 SQL 規範所要求的。

此外,即使特定的 DBMS 總是按給出的 GROUP BY 子句排序資料,使用者也可能會要求以不同的順序排序。就因為你以某種方式分組資料(獲得特定的分組聚集值),並不表示你需要以相同的方式排序輸出。

應該提供明確的 ORDER BY 子句,即使其效果等同於 GROUP BY 子句。

提示:不要忘記 ORDER BY

一般在使用 GROUP BY 子句時,應該也給出 ORDER BY 子句。這是保證資料正確排序的唯一方法。千萬不要僅依賴 GROUP BY 排序資料。

為說明 GROUP BYORDER BY 的使用方法,來看一個例子。

下面的 SELECT 語句類似於前面那些例子。它檢索包含三個或更多物品的訂單號和訂購物品的數目:

SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3;

輸出:

order_num     items
---------     -----
20006         3
20007         5
20008         5
20009         3

要按訂購物品的數目排序輸出,需要新增 ORDER BY 子句,如下所示:

SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3
ORDER BY items, order_num;

輸出:

order_num     items
---------     -----
20006         3
20009         3
20007         5
20008         5

在這個例子中,使用 GROUP BY 子句按訂單號(order_num 列)分組資料,以便 COUNT(*) 函式能夠返回每個訂單中的物品數目。

HAVING 子句過濾資料,使得只返回包含三個或更多物品的訂單。最後,用 ORDER BY 子句排序輸出。

五、SELECT 子句順序

下面回顧一下 SELECT 語句中子句的順序。表 2 以在 SELECT 語句中使用時必須遵循的次序,列出迄今為止所學過的子句。

表 2 SELECT 子句及其順序

子句 說明 是否必須使用
SELECT 要返回的列或表示式
FROM 從中檢索資料的表 僅在從表選擇資料時使用
WHERE 行級過濾
GROUP BY 分組說明 僅在按組計算聚集時使用
HAVING 組級過濾
ORDER BY 輸出排序順序

六、小結

本文介紹瞭如何使用 GROUP BY 子句對多組資料進行彙總計算,返回每個組的結果。

我們看到了如何使用 HAVING 子句過濾特定的組,還知道了 ORDER BYGROUP BY 之間以及 WHEREHAVING 之間的差異。

原文連結:https://www.developerastrid.com/sql/sql-grouping-data/

(完)