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
去導航目錄篇下載創建本系列博文通用庫表及數據的 SQL 語句
本文鏈接:http://www.cnblogs.com/hanzongze/p/tsql-aggregate.html
版權聲明:本文為博客園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文鏈接!個人博客,能力有限,若有不當之處,敬請批評指正,謝謝!