1. 程式人生 > >3 聚合與排序

3 聚合與排序

where子句 理解 dbms 數據量 相同 數據 cin 大小 基準

3 聚合與排序

3-1 對表進行聚合查詢

聚合函數

通過SQL對數據進行 操作或計算時需要使用函數。

計算表中全部數據行數時,可以使用COUNT函數。

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

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

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

MAX :計算表中任意列中數據的最大值。

MIN :計算表中任意列中數據的最小值。

如上所示,用於合計的函數稱為聚合函數或者集合函數。本書中統稱為聚合函數。所謂聚合,就是將多行匯總為一行。

函數這個詞,與我們在學校課上學到的意思是一樣的,就像是輸入某個值就能輸出相應結果的盒子一樣。

計算表中數據的行數

/*

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

sum 計算表中數值列的數據合計值

avg 計算表中數值列的數據平均值

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

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

*/

-- 此處的輸入值稱為參數或者parameter,輸出值稱為返回值

SELECT COUNT(*) FROM shohin;

SELECT COUNT(shiire_tanka) FROM shohin;

即使對同一張表使用COUNT函數,輸入的參數不同得到的結果也會不同。

星號對於COUNT函數來說是獨有的,其他函數並不能將星號作為參數(如果使用星號會出錯)。

計算NULL以外數據的行數

SELECT COUNT(shiire_tanka) FROM shohin;

對於COUNT函數來說,參數列不同計算的結果也會發生變化。

法則3-1

COUNT函數的結果根據參數的不同而不同,COUNT(*) 會得到包含NULL的數據行數,而COUNT(<列名>)會得到NULL之外的數據行數。

計算合計值

SELECT SUM(hanbai_tanka) FROM shohin;

SELECT SUM(shiire_tanka) FROM shohin;

法則3-2

聚合函數會將NULL排除在外。但COUNT(*)例外,並不會排除NULL。

計算平均值

-- (值的合計)/(值的個數)就是平均值的計算公式 如果遇到空的情況會先剔除null 值的合計和值的個數都需要去除。

SELECT AVG(hanbai_tanka), AVG(shiire_tanka) FROM shohin;

計算最大值和最小值

SELECT MAX(hanbai_tanka), MIN(shiire_tanka) FROM shohin;

SELECT MAX(torokubi), MIN(torokubi) FROM shohin;

法則3-3

MAX/MIN函數幾乎適用於所有數據類型的列。SUM/AVG函數只適用於數值類型的列。

使用聚合函數刪除重復值(關鍵字DISTINCT)

SELECT COUNT(DISTINCT shohin_bunrui) FROM shohin;

-- 請註意,這時DISTINCT必須寫在括號中。這是因為必須要在計算行數之前刪除shohin_bunrui列中的重復數據。

SELECT SUM(hanbai_tanka), SUM(DISTINCT hanbai_tanka) FROM shohin;

SELECT DISTINCT COUNT(shohin_bunrui) FROM shohin;

-- DISTINCT不僅限於COUNT函數,所有的聚合函數都可以使用。

法則3-4

想要計算值的種類時,可以在COUNT函數的參數中使用DISTINCT。

法則3-5

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

3-2 對表進行分組

目前為止,我們看到的聚合函數的使用方法,無論是否包含NULL,無論是否刪除了重復數據,都是針對表中的所有數據進行的聚合處理。下面,我們先把表分成幾組,然後再進行聚合處理。

GROUP BY子句

/*

SELECT <列名1>, <列名2>, ...... FROM <表名> GROUP BY <列名1>, <列名2>, ......;

*/

SELECT shohin_bunrui, COUNT(*) FROM shohin;

SELECT shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_bunrui;

這樣,GROUP BY子句就像切蛋糕那樣將表進行分組。在GROUP BY子句中指定的列稱為聚合鍵或者分組列。

當然,GROUP BY子句也和SELECT子句一樣,可以通過逗號分隔指定多列。

法則3-6

GROUP BY就像是切分表的一把刀。

法則3-7

SQL子句的順序不能改變,也不能互換位置。

子句的書寫順序(暫定) :SELECT > FROM > WHERE > GROUP BY

聚合鍵中包含NULL的情況

SELECT shiire_tanka, COUNT(*) FROM shohin GROUP BY shiire_tanka;

-- 當聚合鍵中包含null時,也會將null作為一組特定的數據 這裏的null 大家可以理解為“不確定”

SELECT shiire_tanka, COUNT(*) FROM shohin GROUP BY shiire_tanka;

從結果我們可以看出,當聚合鍵中包含NULL時,也會將NULL作為一組特定的數據。這裏的NULL,大家可以理解為“不確定”。

法則3-8

聚合鍵中包含NULL時,在結果中會以“不確定”行(空行)的形式表現出來。

使用WHERE子句時GROUP BY的執行結果

/*

SELECT <列名1>, <列名2>, ...... FROM <表名> WHERE ... GROUP BY <列名1>, <列名2>, ......;

*/

SELECT shiire_tanka, COUNT(*) FROM shohin WHERE shohin_bunrui = ‘衣服‘;

SELECT shiire_tanka, COUNT(*) FROM shohin WHERE shohin_bunrui = ‘衣服‘ GROUP BY shiire_tanka;

像這樣使用WHERE子句進行聚合處理時,會先根據WHERE子句指定的過濾條件進行過濾,然後再進行聚合處理。

GROUP BY和WHERE並用時SELECT語句的執行順序 :FROM > WHERE > GROUP BY > SELECT

與聚合函數和GROUP BY子句有關的常見錯誤

-- 其他dbms可能報錯

SELECT shohin_mei, shiire_tanka, COUNT(*) FROM shohin GROUP BY shiire_tanka;

SELECT shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_bunrui;

SELECT shohin_bunrui AS sb, COUNT(*) FROM shohin GROUP BY sb;

SELECT shohin_bunrui, COUNT(*) FROM shohin WHERE COUNT(2) GROUP BY shohin_bunrui;

法則3-9

使用GROUP BY子句時,SELECT子句中不能出現聚合鍵之外的列名。

法則3-10

在GROUP BY子句中不能使用SELECT子句中定義的別名。

法則3-11

GROUP BY子句結果的顯示是無序的。

法則3-12

只有SELECT子句和HAVING子句(以及ORDER BY子句)中能夠使用聚合函數。

DISTINCT和GROUP BY

-- DISTINCT和GROUP BY能夠實現相同的功能

SELECT DISTINCT shohin_bunrui FROM shohin;

SELECT shohin_bunrui FROM shohin GROUP BY shohin_bunrui;

-- 除次之外,它們還都會把NULL作為一個獨立的結果返回,對多列使用時也會得到完全相同的結果。其實不僅處理結果相同,執行速度也基本差不多,到底應該使用哪一個呢?

但其實這個問題本身就是本末倒置的,我們應該考慮的是該SELECT語句是否滿足需求。選擇的標準其實非常簡單,在“想要刪除選擇結果中的重復記錄”時使用DISTINCT,在“想要計算聚合結果”時使用GROUP BY。

不適用COUNT等聚合函數,而只使用GROUP BY子句的SELECT語句,會讓人覺得非常奇怪。難免使人產生“到底為什麽要對表進行分組呢?這樣做有必要嗎?”等疑問。

3-3 為聚合結果指定條件

HVING子句

/*

SELECT <列名1>, <列名2>, ...... FROM <表名> GROUP BY <列名1>, <列名2>, ...... HAVING <分組結果對應的條件>;

*/

SELECT shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_bunrui;

SELECT shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_bunrui HAVING COUNT(*) = 2;

SELECT shohin_bunrui, AVG(hanbai_tanka) FROM shohin GROUP BY shohin_bunrui;

SELECT shohin_bunrui, AVG(hanbai_tanka) FROM shohin GROUP BY shohin_bunrui HAVING AVG(hanbai_tanka) >= 2500;

說到指定條件,估計大家都會首先想到WHERE子句。但是,WHERE子句只能指定記錄(行)的條件,而不能用來指定組的條件(例如,“數據行數為2行”或者“平均值為500等”)。

HAVING子句必須寫在GROUP BY子句之後。其在DBMS內部的執行順序也排在GROUP BY子句之後。

使用HAVING子句時SELECT語句的順序 :SELECT > FROM > WHERE > GROUP BY > HAVING

法則3-13

HAVING子句要寫在GROUP BY子句之後。

HAVING子句的構成要素

-- HAVING子句中能夠使用的3種要素:常數/聚合函數/GROUP BY子句中指定的列名(即聚合鍵)

SELECT shohin_mei, shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_mei HAVING shohin_mei = ‘圓珠筆‘;

相當於HAVING子句,更適合寫在WHERE子句中的條件

SELECT shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_bunrui HAVING shohin_bunrui = ‘衣服‘;

SELECT shohin_bunrui, COUNT(*) FROM shohin WHERE shohin_bunrui = ‘衣服‘ GROUP BY shohin_bunrui;

也許有的讀者已經發現了,有些條件既可以寫在HAVING子句當中,有可以寫在WHERE子句當中。這些條件就是聚合鍵所對應的條件。

如果僅從結果來看的話,確實如此。但筆者卻認為,聚合鍵所對應的條件還是應該書寫在WHERE子句之中。

根本原因是where子句和having子句的作用不同,如前所述,having子句是用來指定“組”的條件的,因此,“行”所對應的條件還是應該寫在where子句當中,這樣一來,書寫出的select語句不但可以分清兩者各自的功能,理解起來也更容易

Where子句 = 指定行所對應的條件

Having子句 = 指定組所對應的條件

其次where執行速度快一點

通常情況下,為了得到相同的結果,將條件寫在WHERE子句中要比寫在HAVING子句中的處理速度更快,返回結果所需時間更短。

為了理解其中原因,就要從DBMS的內部運行機制來考慮。使用COUNT函數等對表中的數據進行聚合操作時,DBMS內部就會進行排序處理。排序處理會大大增加機器的負擔,此即所謂高負荷的處理。因此,只有盡可能減少排序的行數,才能增加處理速度。

通過WHERE子句指定條件時,由於排序之前就對數據進行了過濾,所以能夠減少排序的數據量。但HAVING子句是在排序之後才對數據進行分組的,因此與在WHERE子句中指定條件比起來,需要排序的數據量就會多得多。雖然DBMS的內部處理不盡相同,但是對於排序處理來說,基本上都是一樣的。

此外,WHERE子句更具速度優勢的另一個理由是,可以對WHERE子句指定條件所對應的列創建索引,這樣也可以大幅度提高處理速度。創建索引是一種非常普遍的提高DBMS性能的方法,效果也十分明顯,這對WHERE子句來說也十分有利。

法則3-14

聚合鍵所對應的條件不應該書寫在HAVING子句當中,而應該書寫在WHERE子句當中。

3-4 對查詢結果進行排序

通常,從表中抽取數據時,如果沒有特別指定順序,最終排列順序便無從得知。即使是同一條SELECT語句,每次執行時排列順序很可能發生改變。

但是不進行排序,很可能出現結果混亂的情況。這時,便需要通過在SELECT語句末尾添加ORDER BY子句來明確指定排列順序。

ORDER BY子句

-- SELECT <列名1>, <列名2>, ...... FROM <表名> ORDER BY <排序基準列1>, <排序基準列2>, ...;

SELECT shohin_id, shohin_mei, hanbai_tanka, shiire_tanka FROM shohin;

SELECT shohin_id, shohin_mei, hanbai_tanka, shiire_tanka FROM shohin ORDER BY hanbai_tanka;

不論何種情況,ORDER BY子句都需要寫在SELECT語句的末尾。這是因為對數據行進行排序的操作必須在結果即將返回時執行。ORDER BY子句中書寫的列名稱為排序鍵。

子句的書寫順序 :SELECT > FROM > WHERE > GROUP BY > HAVING > ORDER BY

法則3-15

ORDER BY子句通常寫在SELECT語句的末尾。

指定升序(ASC)和降序(DESC)

SELECT shohin_id, shohin_mei, hanbai_tanka, shiire_tanka FROM shohin ORDER BY hanbai_tanka DESC;

SELECT shohin_id, shohin_mei, hanbai_tanka, shiire_tanka FROM shohin ORDER BY hanbai_tanka ASC;

由於ASC和DESC這兩個關鍵字是以列為單位指定的,所以可以同時指定一個列為升序,指定其他列為降序。

法則3-16

未指定ORDER BY子句中排列順序時會默認使用升序進行排列。

指定多個排序鍵

SELECT shohin_id, shohin_mei, hanbai_tanka, shiire_tanka FROM shohin ORDER BY hanbai_tanka, shohin_id;

這樣一來,就可以在ORDER BY子句中同時指定多個排序鍵了。會優先使用左側的鍵,如果該列存在相同值的話,會接著參考右側的鍵。當然,也可以同時使用3個以上的排序鍵。

NULL的順序

SELECT shohin_id, shohin_mei, hanbai_tanka, shiire_tanka FROM shohin ORDER BY shiire_tanka;

不能對NULL使用比較運算符,也就是說,不能對NULL和數字進行排序。也不能與字符串和日期比較大小。因此,使用含有NULL的列作為排序鍵時,NULL會在結果的開頭或末尾匯總顯示。

究竟是在開頭顯示還是在末尾顯示,並沒有特殊規定。某些DBMS中可以指定NULL在開頭或末尾顯示,希望大家對自己使用的DBMS的功能研究以下。

法則3-17

排序鍵中包含NULL時,會在開頭或末尾進行匯總。

在排序鍵中使用顯示用別名

SELECT shohin_id AS id, shohin_mei, hanbai_tanka AS ht, shiire_tanka FROM shohin ORDER BY ht, id;

不能在GROUP BY子句中使用的別名,為什麽可以在ORDER BY子句中使用呢?這是因為SQL語句在DBMS內部的執行順序被掩蓋起來了。

使用HAVING子句時SELECT語句執行順序 :FROM > WHERE >GROUP BY > HAVING > SELECT > ORDER BY

這只是一個粗略的總結,雖然具體的執行順序根據DBMS的不同而不同,但是大家有這樣一個大致的印象就可以了。一定要記住SELECT子句的執行順序在GROUP BY子句之後,ORDER BY子句之前。因此,在執行GROUP BY子句時,SELECT語句中定義的別名無法被識別。對於在SELECT子句之後執行的ORDER BY子句來說,就沒有這樣的問題了。

法則3-18

在ORDER BY子句中可以使用SELECT子句中定義的別名。

ORDER BY 子句中可以使用存在於表中,但並不包含在SELECT子句中的列。除此之外,還可以使用聚合函數。

ORDER BY子句中可以使用的列

SELECT shohin_mei, hanbai_tanka, shiire_tanka FROM shohin ORDER BY shohin_id;

SELECT shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_bunrui ORDER BY COUNT(*);

法則3-19

在ORDER BY子句中可以使用SELECT子句中未使用的列和聚合函數。

不要使用列編號

-- ORDER BY 子句中可以使用列的編號,列編號是指SELECT子句中的列按照從左到右的順序進行排列時所對應的編號(1,2,3,…)。 不建議使用編號 代碼閱讀起來比較難 該排序功能將來會被刪除

-- 使用列名

SELECT shohin_id, shohin_mei, hanbai_tanka, shiire_tanka FROM shohin ORDER BY hanbai_tanka DESC, shohin_id;

-- 使用列編號

SELECT shohin_id, shohin_mei, hanbai_tanka, shiire_tanka FROM shohin ORDER BY 3 DESC, 1;

雖然列編號使用起來非常方便,但我們並不推薦使用,原因有以下兩點 :第一,代碼閱讀起來比較難;第二,這也是根本問題。實際上,在SQL-92中已經明確指出該排序功能將來會被刪除。

法則3-20

在ORDER BY子句中不要使用列編號。

3 聚合與排序