1. 程式人生 > >SQL Server溫故系列(4):SQL 查詢之集合運算 & 聚合函式

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
版權宣告:本文為部落格園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文連結!個人部落格,能力有限,若有不當之處,敬請批評指正,謝謝!