首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server GROUP BY 進階匯總五大招:單欄/多欄/排序/NULL/運算式/HAVING 全掌握

     編輯:關於SqlServer

範圍資料庫:AdventureWorks2012

所有範例基於 `Sales.SalesOrderDetail` (明細) + `Sales.SalesOrderHeader` (主單)。


招式一:單欄分組 + 聚合

SELECT CarrierTrackingNumber,
       SUM(LineTotal) AS TotalLine
FROM Sales.SalesOrderDetail
GROUP BY CarrierTrackingNumber;
  • 結果 3,807 群組。
  • 只有出現在 `GROUP BY` 的欄位才能直接出現在 `SELECT` 列表 (未聚合時)。

加 WHERE 過濾再分組

SELECT CarrierTrackingNumber, SUM(LineTotal) AS TotalLine
FROM Sales.SalesOrderDetail
WHERE CarrierTrackingNumber = '48F2-4141-9A'
GROUP BY CarrierTrackingNumber;

`WHERE` 在分組**前**過濾行,減少聚合輸入。


招式二:多欄分組 + 排序

SELECT D.ProductID, H.OrderDate, SUM(D.LineTotal) AS TotalLine
FROM Sales.SalesOrderDetail D
JOIN Sales.SalesOrderHeader H ON D.SalesOrderID = H.SalesOrderID
GROUP BY D.ProductID, H.OrderDate
ORDER BY TotalLine DESC;
  • 依 `(ProductID, OrderDate)` 唯一組合分組。
  • `ORDER BY` 可使用別名 `TotalLine`,執行於分組**後**。

招式三:NULL 值分組行為

CREATE TABLE #NullTest (OrderDate DATE, Amount INT);
INSERT #NullTest VALUES (NULL,100), ('2024-10-30',100), ('2024-10-31',100), (NULL,100);

SELECT OrderDate, SUM(Amount) AS Total
FROM #NullTest
GROUP BY OrderDate;
-- 結果:NULL 群組合併為一行 (Total=200),SQL Server 視所有 NULL 相等。

DROP TABLE #NullTest;

招式四:GROUP BY 運算式 (動態維度)

SELECT CONVERT(CHAR(7), H.OrderDate, 120) AS YearMonth, -- yyyy-MM
       SUM(D.LineTotal) AS TotalLine
FROM Sales.SalesOrderDetail D
JOIN Sales.SalesOrderHeader H ON D.SalesOrderID = H.SalesOrderID
GROUP BY CONVERT(CHAR(7), H.OrderDate, 120)
ORDER BY TotalLine DESC;
  • 直接在 `GROUP BY` 寫運算式 (轉換、運算、函數),無需子查詢/CTE。
  • 同一運算式必須在 `SELECT` 與 `GROUP BY` **完全一致** (含風格代碼 120)。

招式五:HAVING 過濾聚合結果

SELECT D.ProductID, H.OrderDate, SUM(D.LineTotal) AS TotalLine
FROM Sales.SalesOrderDetail D
JOIN Sales.SalesOrderHeader H ON D.SalesOrderID = H.SalesOrderID
GROUP BY D.ProductID, H.OrderDate
HAVING SUM(D.LineTotal) > 200000
ORDER BY TotalLine DESC;
  • `HAVING` 在分組**後**過濾群組,可使用聚合函數。
  • `WHERE` 不能用聚合;`HAVING` 不能用未分組未聚合的欄位 (除非在聚合內)。

進階組合:ROLLUP / CUBE / GROUPING SETS (SQL 2008+)

-- 多維度小計一次搞定
SELECT
    ISNULL(CAST(ProductID AS VARCHAR), 'ALL') AS ProductID,
    ISNULL(CONVERT(CHAR(7), OrderDate, 120), 'ALL') AS YearMonth,
    SUM(LineTotal) AS TotalLine,
    GROUPING(ProductID) AS GP_Prod,
    GROUPING(OrderDate) AS GP_Date
FROM Sales.SalesOrderDetail D
JOIN Sales.SalesOrderHeader H ON D.SalesOrderID = H.SalesOrderID
GROUP BY ROLLUP (ProductID, OrderDate); -- 或 CUBE, GROUPING SETS ((ProductID), (OrderDate), ())
  • `ROLLUP`:產生層級小計 (ProductID → ProductID+OrderDate → 總計)。
  • `CUBE`:所有維度組合小計。
  • `GROUPING()`:區分「真實 NULL」與「小計行產生的 NULL」。

效能提示

  1. 分組欄位建立索引 (覆蓋索引更佳)。
  2. 大表先 `WHERE` 縮小範圍再 `GROUP BY`。
  3. 避免 `SELECT *`,僅選需要欄位減少記憶體授予。
  4. `HAVING` 條件若可下推轉為 `WHERE` (如 `COUNT(*) > 1` 且有唯一鍵),優化器可能自動轉換。

總結

`GROUP BY` 核心三要素:**分組鍵、聚合函數、過濾時機 (WHERE vs HAVING)**。掌握運算式分組、NULL 語義、ROLLUP/CUBE 多維小計,即可應對 90% 報表匯總需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved