SQL Server溫故系列(4):SQL 查詢之集合運算 & 聚合函式
- 1、集合運算
- 1.1、並集運算 UNION
- 1.2、差集運算 EXCEPT
- 1.3、交集運算 INTERSECT
- 1.4、集合運算小結
- 2、聚合函式
- 2.1、求行數函式 COUNT
- 2.2、求和函式 SUM
- 2.3、求最大值函式 MAX
- 2.4、求最小值函式 MIN
- 2.5、求平均值函式 AVG
- 2.6、聚合函式小結
- 3、本文小結
1、集合運算
在數學中,不僅可以對指定的數字個體做四則運算,還可以對指定的集合整體做交併補運算。類似的,在資料庫中也是不僅可以對具體的資料行進行增刪改查,還可以對查詢結果集進行集合運算。SQL Server 中的集合運算有並集運算、差集運算和交集運算三種,本節將逐一講述。
1.1、並集運算 UNION
並集運算子 UNION 的作用是將兩個或多個查詢的結果集合併為單個結果集。在 UNION 運算中,需要確保各個子結果集的欄位數相同、欄位的順序相同、欄位的資料型別相容。合併結果集的列名始終會取第一個子結果集中的列名,因此,如果要對合並結果集排序,則需要用第一個子結果集的欄位名。
引數 ALL 表示將全部行併入結果集中,換句話說合並結果集可能會包含重複行;相反,如果未指定該引數,則會刪除重複行。需要注意的是,在做重複判斷時,UNION 會把兩個 NULL 值被視為相等的。因為 UNION ALL 不需要刪除重複行,所以效能比 UNION 要好。因此,除非必須要刪除重複行,否則建議一律使用 UNION ALL。
1.1.1、簡單 UNION ALL。查詢 1 班的男生和 2 班的女生。示例如下:
SELECT * FROM T_Students t1 WHERE t1.ClassId = 1 AND t1.Gender = 1
UNION ALL
SELECT * FROM T_Students t2 WHERE t2.ClassId = 2 AND t2.Gender = 0;
1.1.2、簡單 UNION ALL。查詢 1 班的男生和該班的男生人數。示例如下:
SELECT 1 cnt,t1.Code,t1.Name FROM T_Students t1 WHERE t1.ClassId = 1 AND t1.Gender = 1 UNION ALL SELECT COUNT(1),NULL,NULL FROM T_Students t2 WHERE t2.ClassId = 1 AND t2.Gender = 1;
查詢結果如下:
cnt Code Name
----------- ------------------------------ ------------------------------
1 S330102001 鄭強
1 S330102002 肖俊生
1 S330300007 錢波
1 S330104009 金橋
4 NULL NULL
1.1.3、UNION 與 ORDER BY。查詢 1 班的男生和 2 班的女生,並且將最終結果集按年齡從大到小排序。示例如下:
SELECT * FROM T_Students t1 WHERE t1.ClassId = 1 AND t1.Gender = 1
UNION ALL
SELECT * FROM T_Students t2 WHERE t2.ClassId = 2 AND t2.Gender = 0
ORDER BY t1.Birthday;
1.1.4、UNION 與 SELECT INTO。建立一個活動學生表,並將 1 班的男生和 2 班的女生加入其中。示例如下:
SELECT t1.Code,t1.Name,t1.Gender,t1.Birthday
INTO T_ActivityStudents
FROM T_Students t1
WHERE t1.ClassId = 1 AND t1.Gender=1
UNION ALL
SELECT t2.Code,t2.Name,t2.Gender,t2.Birthday
FROM T_Students t2
WHERE t2.ClassId = 2 AND t2.Gender=0;
1.1.5、通過括號來改變 UNION 的運算順序。示例如下:
WITH t AS(
SELECT TOP(3) * FROM T_Students t WHERE t.ClassId = 1 ORDER BY t.Birthday
)
SELECT * FROM t
UNION ALL(
SELECT * FROM t
UNION
SELECT * FROM t
);
簡單說明一下:首先,上例會返回 6 條資料。其次,很明顯 CTE 中包含了年齡最大的 3 個學生。然後,括號會提升運算子的優先順序,而且 UNION 會刪除重複行,所以括號中會返回 3 條資料。最後,UNION ALL 不會刪除重複行,所以最終返回的 6 條資料是第一個查詢返回的 3 條資料加上括號中兩個查詢共同產生的 3 條資料。
1.2、差集運算 EXCEPT
差集運算子 EXCEPT 的作用是比較兩個查詢的結果集,然後返回左側結果集包含但右側結果集不包含的行,且不包含重複行。與 UNION 運算相同,EXCEPT 運算也要求各個子結果集的欄位數相同、順序相同、型別相容。且 EXCEPT 也會把兩個 NULL 值被視為相等的。示例如下:
SELECT * FROM T_GoodStudents t1 WHERE t1.Gender = 0
EXCEPT
SELECT * FROM T_GoodStudents t2 WHERE t2.Birthday < '2000-01-01';
注意:上例是用女生這個結果集減去非 00 後學生結果集,但得到的結果集並不一定是 00 後女生,因為女生的出生日期可能是 NULL,所以最終的結果集是 00 後女生加上出生日期為 NULL 的女生。
類似於 UNION:EXCEPT 也可以與 ORDER BY 連用來給差集(最終的結果集)排序,而且差集的列名也跟第一個子結果集的列名相同。EXCEPT 還可以與 SELECT INTO 連用將差集拷貝到一個新表中。當有多個 EXCEPT 時也可以通過括號來改變運算順序。具體用法可參考 UNION 的示例。
1.3、交集運算 INTERSECT
交集運算子 INTERSECT 的作用是比較兩個查詢的結果集,然後返回左側結果集和右側結果集都包含的行,且不包含重複行。與 UNION 運算相同,INTERSECT 運算也要求各個子結果集的欄位數相同、順序相同、型別相容。且 INTERSECT 也會把兩個 NULL 值被視為相等的。示例如下:
SELECT * FROM T_GoodStudents t1 WHERE t1.Gender = 0
INTERSECT
SELECT * FROM T_GoodStudents t2 WHERE t2.Birthday < '2000-01-01';
注意:上例是取女生這個結果集和非 00 後學生結果集的交集,但得到的結果集並不一定是非 00 後女生,因為女生的出生日期可能是 NULL,所以最終的結果集是非 00 後女生加上出生日期為 NULL 的女生。
類似於 UNION:INTERSECT 也可以與 ORDER BY 連用來給交集(最終的結果集)排序,而且交集的列名也跟第一個子結果集的列名相同。INTERSECT 還可以與 SELECT INTO 連用將交集拷貝到一個新表中。當有多個 INTERSECT 時也可以通過括號來改變運算順序。具體用法可參考 UNION 的示例。
1.4、集合運算小結
上文逐一講述了各個集合運算子的語法和用途。其實這些集合運算子不僅可以單獨使用,還可以結合起來使用,示例如下:
SELECT t1.Id,t1.Name,t1.Gender,t1.Birthday FROM T_Students t1 WHERE t1.ClassId = 1
UNION ALL
SELECT t2.Id,t2.Name,t2.Gender,t2.Birthday FROM T_Students t2 WHERE t2.ClassId = 3
EXCEPT
SELECT * FROM T_GoodStudents t3 WHERE t3.Birthday < '2000-01-01'
INTERSECT
SELECT * FROM T_GoodStudents t4 WHERE t4.Gender = 1;
注意:上例的運算順序並不是先 UNION ALL,然後 EXCEPT,再 INTERSECT。因為 INTERSECT 比 EXCEPT 和 UNION 的優先順序要高,而 EXCEPT 與 UNION 的優先順序相同,所以上例的實際運算順序是先 INTERSECT,然後 UNION ALL,最後再用 UNION ALL 的結果集減 INTERSECT 的結果集,即最後進行 EXCEPT 運算。
2、聚合函式
聚合函式的作用是對一組值執行計算,並返回單個結果值。聚合函式只能在 SELECT 子句或 HAVING 子句中作為表示式來用。求行數函式 COUNT、求和函式 SUM、求最大值函式 MAX、求最小值函式 MIN、求平均值函式 AVG,這 5 個函式是最常用的聚合函式,主流的關係型資料庫也都支援它們。
其實常見的關係型資料庫都支援很多聚合函式,但其中大部分都是非標準的,各個資料庫之間的差別也比較大,而且這些函式也都不常用。個人建議實際工作中不要用這些函式,以免跟某個具體的資料庫綁死。
2.1、求行數函式 COUNT
求行數函式 COUNT 會返回查詢結果集的行數。COUNT 函式中可以是一個具體的列,也可以是代表所有列的星號,還可以是一個具體的常量或變數。示例如下:
SELECT COUNT(t.Id) result FROM T_Students t; -- result:32 rows
SELECT COUNT(*) result FROM T_Students t; -- result:32 rows
SELECT COUNT(1) result FROM T_Students t; -- result:32 rows
COUNT 是所有聚合函式中唯一不會忽略 NULL 值的函式,但如果被計算列本身含有 NULL 值是會被忽略的。示例如下:
SELECT COUNT(Remark) result FROM T_Students t; -- result:28 rows
在 COUNT 函式中新增 DISTINCT 引數,表示會先去除重複行,然後統計剩下的非 NULL 且唯一的值的個數。示例如下:
SELECT COUNT(DISTINCT Remark) result FROM T_Students t; -- result:26 rows
注意:COUNT 函式的返回值一定是大於或等於 0 的整數,證明如下:
SELECT COUNT(*) result; -- result:1 rows
SELECT COUNT(*) result WHERE 1 = 2; -- result:0 rows
另外,從 SQL Server 2008 開始,官方在增加了一個COUNT_BIG
函式,它的用法和用途與 COUNT 完全相同,唯一不同的就是返回值的型別。COUNT 函式總是返回一個 INT 型別的整數,而COUNT_BIG
函式總是返回一個 BIGINT 型別的整數。示例如下:
SELECT COUNT_BIG(1) result FROM T_Students t; -- result:32 rows
SELECT COUNT_BIG(Remark) result FROM T_Students t; -- result:28 rows
SELECT COUNT_BIG(DISTINCT Remark) result FROM T_Students t; -- result:26 rows
2.2、求和函式 SUM
求和函式 SUM 會返回表示式中所有值的和。SUM 函式會忽略所有 NULL 值,且只能應用於數字型別的欄位。例如要查詢學生 1 第 1 次考試的總分,示例如下:
SELECT SUM(t.Scores) FROM T_ExamResults t WHERE t.StudentId = 1 AND t.Counts = 1;
在 SUM 函式中新增 DISTINCT 引數,表示會先去除重複行,然後統計剩下的非 NULL 且唯一的數值之和。示例如下:
SELECT SUM(t.Scores) result FROM T_ExamResults t WHERE t.StudentId = 6; -- result:2202.5
SELECT SUM(DISTINCT t.Scores) result FROM T_ExamResults t WHERE t.StudentId = 6; -- result:1857.5
注意:SUM 函式的返回值有可能會是 NULL 值,證明如下:
SELECT SUM(1) result; -- result:1
SELECT SUM(1) result WHERE 1 = 2; -- result:NULL
2.3、求最大值函式 MAX
求最大值函式 MAX 會返回表示式中的最大值。MAX 函式會忽略所有 NULL 值。例如要查詢學生 1 課程 1 的歷次考試最高分,示例如下:
SELECT MAX(t.Scores) FROM T_ExamResults t WHERE t.StudentId = 1 AND t.Counts = 1;
MAX 函式還可以作用於日期型別或字元型別,此時 MAX 將按照日期數值或字元排序順序來確定最大值。示例如下:
SELECT MAX(t.Birthday) FROM T_Students t; -- 數值最大的出生日期,年齡最小
SELECT MAX(t.Name) FROM T_Students t; -- 字元排序最靠後的姓名
注意:MAX 函式的返回值有可能會是 NULL 值,證明如下:
SELECT MAX(1) result; -- result:1
SELECT MAX(1) result WHERE 1 = 2; -- result:NULL
2.4、求最小值函式 MIN
求最小值函式 MIN 會返回表示式中的最小值。MIN 函式會忽略所有 NULL 值。例如要查詢學生 1 課程 1 的歷次考試最低分,示例如下:
SELECT MIN(t.Scores) FROM T_ExamResults t WHERE t.StudentId = 1 AND t.Counts = 1;
MIN 函式還可以作用於日期型別或字元型別,此時 MAX 將按照日期數值或字元排序順序來確定最小值。示例如下:
SELECT MIN(t.Birthday) FROM T_Students t; -- 數值最小的出生日期,年齡最大
SELECT MIN(t.Name) FROM T_Students t; -- 字元排序最靠前的姓名
注意:MIN 函式的返回值有可能會是 NULL 值,證明如下:
SELECT MIN(1) result; -- result:1
SELECT MIN(1) result WHERE 1 = 2; -- result:NULL
2.5、求平均值函式 AVG
求平均值函式 AVG 會返回表示式中所有值的平均值。AVG 函式會忽略所有 NULL 值,且只能應用於數字型別的欄位。例如要查詢學生 1 第 1 次考試的平均分,示例如下:
SELECT AVG(t.Scores) FROM T_ExamResults t WHERE t.StudentId = 1 AND t.Counts = 1;
在 AVG 函式中新增 DISTINCT 引數,表示會先去除重複行,然後統計剩下的非 NULL 且唯一的數值之和。示例如下:
SELECT AVG(t.Scores) result FROM T_ExamResults t WHERE t.StudentId = 6; -- result:73.416666
SELECT AVG(DISTINCT t.Scores) result FROM T_ExamResults t WHERE t.StudentId = 6; -- result:74.300000
注意:AVG 函式的返回值有可能會是 NULL 值,證明如下:
SELECT AVG(1) result; -- result:1
SELECT AVG(1) result WHERE 1 = 2; -- result:NULL
2.6、聚合函式小結
上文逐一講述了常見五大聚合函式的基本語法和用途。其實這些聚合函式不僅可以單獨使用,還可以結合起來使用。
示例一、查詢第 1 次課程 2 考試成績的統計結果:
SELECT COUNT(1) 參與人數,
SUM(t.Scores) 總分,MAX(t.Scores) 最高分,MIN(t.Scores) 最低分,AVG(t.Scores) 平均分
FROM T_ExamResults t
WHERE t.Counts = 1 AND t.CourseId = 2;
示例二、查詢 1 班的學生總數及年齡統計結果:
WITH t AS(
SELECT t.Code,t.Name,DATEDIFF(YEAR,t.Birthday,GETDATE()) Age
FROM T_Students t
WHERE t.ClassId = 1
)
SELECT COUNT(1) 學生個數,MAX(t.Age) 最大年齡,MIN(t.Age) 最小年齡,AVG(t.Age) 平均年齡
FROM t;
注意:本文所有關於聚合函式的示例,查詢選擇列表中包含的都是聚合函式表示式,沒有一個欄位,因為不允許,不過倒是可以包含與表字段無關的常量或變數。其實聚合函式通常與 GROUP BY 子句一起使用,而且也只有包含在 GROUP BY 子句中的欄位才能出現在查詢選擇列表中,具體原因將在下一篇博文中具體講述。
3、本文小結
本文主要講述了 SQL Server 中的集合運算和聚合函式,以及它們的基本語法和用途。在集合運算中,UNION 和 UNION ALL 是比較常用的。而常見的 5 個聚合函式都比較常用。
本文參考連結:
- 1、SQL Server 2016 Set Operators - UNION
- 2、SQL Server 2016 Set Operators - EXCEPT and INTERSECT
- 3、SQL Server 2016 Aggregate Functions
本文連結:http://www.cnblogs.com/hanzongze/p/tsql-aggregate.html
版權宣告:本文為部落格園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文連結!個人部落格,能力有限,若有不當之處,敬請批評指正,謝謝!