SQLServer 之 Group By 和 Compute By


  創建測試表,如下:

CREATE TABLE tableTest
(
    Id INT PRIMARY KEY,
    DepartMent NVARCHAR(30),
    Name NVARCHAR(30),
    Salary int
)

  添加測試數據,如下圖:

  

一、GROUP BY

  規則1:單值規則,跟在SELECT后面的列表,對於每個分組來說,必須返回且僅僅返回一個值。

  在select指定的字段要么就要包含在Group By語句的后面,作為分組的依據;要么就要被包含在聚合函數中。

SELECT DepartMent, COUNT(1) Num
FROM tableTest
GROUP BY DepartMent

  

  因此,當希望查詢出不是分組依據,且不包含在聚合函數中的字段信息時,要另尋解決方案。

  例如:查詢每個部門,最高工資的那個人的姓名,部門,工資。

  方案1:關聯子查詢

SELECT DepartMent,Name,Salary
FROM tableTest T1
WHERE NOT EXISTS(SELECT DepartMent,Name,Salary
    FROM tableTest T2
    WHERE T2.DepartMent = T1.DepartMent
        AND T2.Salary > T1.Salary
)

 

  

  完全符合要求。對於上面的關聯子查詢,解釋:遍歷工資表的所有記錄,查找不存在比當前記錄部門相同且工資還大的記錄。

  雖然,關聯子查詢的語法非常簡單,但是性能並不好。因為對於每一條記錄,都要執行一次子查詢。

  方案2:衍生表

  使用衍生表的思路是,先執行一個子查詢,得到一個臨時結果集,然后用臨時結果集和原表進行INNER JOIN操作。就能得到最高工資的人的信息。

SELECT T1.DepartMent,T1.Name,T1.Salary
FROM tableTest T1 INNER JOIN 
    ( SELECT DepartMent,MAX(Salary) maxSalary FROM tableTest GROUP BY DepartMent ) T2
    ON T1.DepartMent = T2.DepartMent AND  T1.Salary = T2.maxSalary

  

  衍生表的方式性能優於關聯子查詢,因為衍生表的方式只執行了一次子查詢。但是它需要一張臨時表來存儲臨時記錄。因此,這個方案也並不是最佳的解決方案。

  方案3:使用LEFT JOIN + IS NULL

  這是一個更妙的解決方案,當我們用一個外聯結去匹配記錄時,當匹配的記錄不存在,就會用NULL來代替相應的列。

  先看如下 SQL 語句:

SELECT T1.Id,T1.DepartMent,T1.Name,T1.Salary,T2.Id,T2.DepartMent,T2.Name,T2.Salary
FROM tableTest T1 LEFT JOIN tableTest T2
    ON T1.DepartMent = T2.DepartMent AND  T1.Salary < T2.Salary

  

  從中你看出,當T2表中,不存在比T1表中工資高的記錄時就返回NULL。

  由此,加一個IS NULL條件即可。

SELECT T1.DepartMent,T1.Name,T1.Salary
FROM tableTest T1 LEFT JOIN tableTest T2
    ON T1.DepartMent = T2.DepartMent AND  T1.Salary < T2.Salary
WHERE T2.Id IS NULL

  

  JOIN解決方案適用於針對大量數據查詢並且可伸縮比較時。它總是能比基於子查詢的解決方案更好地適應數據量的變量。

  方案4:對額外的列使用聚合函數(不適合同是有多條數據滿足條件的情況)

    GROUP BY 時,SELECT列表必須返回的是單值,那么我們可以通過使用聚合函數,讓這個列返回單值。

SELECT DepartMent,MAX(Name),MAX(Salary)
FROM tableTest 
GROUP BY DepartMent

  

  其實,返回的數據是有問題的,當工資相同時,它就返回按姓名從大到小排列的第一個姓名。也就是說,當工資相同時,它只能夠返回一條記錄。

  

  方案5:Row_Number() OVER(PARTITION BY partField ORDER BY orderField)

WITH tableB AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY DepartMent ORDER BY Salary DESC) AS part ,DepartMent, Name, Salary
  FROM tableTest
)
SELECT * FROM tableB WHERE part=1

  輸出如下:

  

 

二、Compute 和 Compute By

  GROUP BY子句有個缺點,就是返回的結果集中只有合計數據,而沒有原始的詳細記錄。如果想在SQL SERVER中完成這項工作,可以使用COMPUTE BY子句。COMPTE生成合計作為附加的匯總列出現在結果集的最后。當與BY一起使用時,COMPUTE 子句在結果集內生成控制中斷和分類匯總。

  下列 SELECT 語句使用簡單 COMPUTE 子句生成 tableTest表中 Salary 的求和總計:

SELECT Department, Name, Salary
FROM tableTest
COMPUTE SUM(Salary)

  

  下列查詢在 COMPUTE 子句中加入可選的 BY 關鍵字,以生成每個組的小計:

SELECT Department, Name, Salary
FROM tableTest
ORDER BY Department
COMPUTE SUM(Salary) BY Department

  SELECT 語句的結果用4 個結果集返回,2個組中的每個組都有兩個結果集。每個組的第一個結果集是一個行集,其中包含選擇列表中所請求的信息。每個組的第二個結果集包含 COMPUTE 子句中兩個 SUM 函數的小計。

  

  COMPUTE BY 子句的規則:

  (1)不能將distinct與行統計函數一起使用

  (2)compute ??? by 子句中 ???出的列必須出現在選擇列表中

  (3)不能在含有compute by 子句的語句中使用select into 子句,因為包括compute 子句的語句會產生不規則的行。

  (4)如果使用了compute by子句,則必須使用order by 子句, 而且compute by子句中的列必須包含在order by 子句中,並且對列的前后順序和起始項都要一致(即compute by子句中的列必須是order by子句中列表的全部,或者前邊的連續幾個)。

  (5)如果compute 省略了 by ,則order by 也可以省略

  (6)如果compute by 子句包含多列時,會將一個組(第一個列分的組)分成若干個子組(利用后面的列),並對每層子組進行統計。

  (7)使用多個compute by子句時,會分別按不同的組統計出結果。詳細信息還是按照正常的第一個分組方式顯示。

  (8)compute by 子句中可以使用多個統計函數,他們互不影響

  (9)compute by 子句中可以不包含by ,而只用compute  此時不對前面信息分組,而只對全部信息進行統計。

  在實際開發中compute與compute by的作用並不是很大,SQL Server支持compute和compute by,而Access並不支持。

  在 COMPUTE 或 COMPUTE BY 子句中,不能包含 ntext、text 或 image 數據類型。

三、 COMPUTE 和 GROUP BY 區別

  GROUP BY 生成單個結果集。每個組都有一個只包含分組依據列和顯示該組子聚合的聚合函數的行。選擇列表只能包含分組依據列和聚合函數。

  COMPUTE 生成多個結果集。一類結果集包含每個組的明細行,其中包含選擇列表中的表達式。另一類結果集包含組的子聚合,或 SELECT 語句

的總聚合。選擇列表可包含除分組依據列或聚合函數之外的其它表達式。聚合函數在 COMPUTE 子句中指定,而不是在選擇列表中。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM