SQL Server裏Grouping Sets的威力

在SQL Server裏,你有沒有想進行跨越多個列/緯度的彙集操做,不使用SSAS許可(SQL Server分析服務)。我不是說在生產裏使用開發版,也不是說安裝盜版SQL Server。sql

不可能的任務?未必,由於經過SQL Server裏所謂的Grouping Sets就能夠。在這篇文章裏我會給你歸納介紹下Grouping Sets,使用它們能夠實現哪類查詢,什麼是它們的性能優點。數據庫

使用Grouping Sets的聚合

假設你有個訂單表,你想進行跨多個分組的T-SQL彙集查詢。在AdventureWorks2012數據庫的Sales.SalesOrderHeader表的環境裏,這些分組能夠相似以下:
性能

  • 在每列分組
  • GROUP BY SalesPersonID, YEAR(OrderDate)
  • GROUP BY CustomerID, YEAR(OrderDate)
  • GROUP BY CustomerID, SalesPersonID, YEAR(OrderDate)

當你想用傳統T-SQL查詢進行這些各自分組時,你須要多個語句,對各個記錄集進行UNION ALL。咱們來看這樣的查詢:spa

 1 SELECT * FROM
 2 (
 3     -- 1st Grouping Set
 4     SELECT
 5         NULL AS 'CustomerID',
 6         NULL AS 'SalesPersonID', 
 7         NULL AS 'OrderYear', 
 8         SUM(TotalDue) AS 'TotalDue' 
 9     FROM Sales.SalesOrderHeader
10     WHERE SalesPersonID IS NOT NULL
11 
12     UNION ALL
13 
14     -- 2nd Grouping Set
15     SELECT
16         NULL AS 'CustomerID',
17         SalesPersonID, 
18         YEAR(OrderDate) AS 'OrderYear', 
19         SUM(TotalDue) AS 'TotalDue' 
20     FROM Sales.SalesOrderHeader
21     WHERE SalesPersonID IS NOT NULL
22     GROUP BY SalesPersonID, YEAR(OrderDate)
23 
24     UNION ALL
25 
26     -- 3rd Grouping Set
27     SELECT
28         CustomerID,
29         NULL AS 'SalesPersonID', 
30         YEAR(OrderDate) AS 'OrderYear', 
31         SUM(TotalDue) AS 'TotalDue' 
32     FROM Sales.SalesOrderHeader
33     WHERE SalesPersonID IS NOT NULL
34     GROUP BY CustomerID, YEAR(OrderDate)
35 
36     UNION ALL
37 
38     -- 4th Grouping Set
39     SELECT
40         CustomerID,
41         SalesPersonID,
42         YEAR(OrderDate) AS 'OrderYear', 
43         SUM(TotalDue) AS 'TotalDue' 
44     FROM Sales.SalesOrderHeader
45     WHERE SalesPersonID IS NOT NULL
46     GROUP BY CustomerID, SalesPersonID, YEAR(OrderDate)
47 ) AS t
48 ORDER BY CustomerID, SalesPersonID, OrderYear
49 GO

用這個T-SQL語句方法有多個缺點:code

  • T-SQL語句自己很龐大,由於每一個單獨分組都是一個不一樣查詢。
  • 每查詢1次,Sales.SalesOrderHeader表須要訪問4次。
  • 每查詢1次,你在執行計劃裏會看到SQL Server進行了4次的索引查找(非彙集)(Index Seek (NonClustered) )

 

若是你使用自SQL Server 2008之後引入的grouping sets功能,就能夠大大簡化你須要的T-SQL代碼。下面代碼展現你一樣的查詢,但此次用grouping sets實現。server

 1 SELECT
 2     CustomerID, 
 3     SalesPersonID, 
 4     YEAR(OrderDate) AS 'OrderYear', 
 5     SUM(TotalDue) AS 'TotalDue'
 6 FROM Sales.SalesOrderHeader
 7 WHERE SalesPersonID IS NOT NULL
 8 GROUP BY GROUPING SETS
 9 (
10     -- Our 4 different grouping sets
11     (CustomerID, SalesPersonID, YEAR(OrderDate)),
12     (CustomerID, YEAR(OrderDate)),
13     (SalesPersonID, YEAR(OrderDate)),
14     ()
15 )
16 GO

從代碼自己能夠看到,你只在GROUP BY GROUPING SETS子句裏指定須要的分組集——其它的一切都由SQL Server搞定。指定的空括號是所謂的Empty Grouping Set,是跨整個表的彙集。當你看STATISTICS IO輸出時,你會發現Sales.SalesOrderHeader只被訪問了1次!這是和剛纔手工實現的巨大區別。blog

在執行計劃裏,SQL Server使用了Table Spool運算符,它把得到的數據臨時存儲在TempDb裏。來自臨時表裏建立的Worktable的數據在執行計劃的第2個分支被使用。所以對來自表的每一個分組數據沒有從新掃描,這就給整個執行計劃的帶來了更好的性能。索引

咱們再來看下執行計劃,你會發現查詢計劃包含了3個Stream Aggregate運算符(紅色,藍色,綠色高亮顯示)。這3個運算符計算各個分組集:開發

  • 藍色高亮的運算符計算CustomerID, SalesPersonID, YEAR(OrderDate的分組集。
  • 紅色高亮的運算符計算SalesPersonID, YEAR(OrderDate)的分組集。另外也計算每1列的分組集。
  • 綠色高亮的運算符計算CustomerID, YEAR(OrderDate)的分組集。

2個連續的Stream Aggregate運算符的背後想法是計算所謂的Super Aggregates——彙集的彙集。get

小結

在今天的文章裏我給你介紹了grouping sets,在SQL Server 2008後引入的加強T-SQL。如你所見grouping sets有2個大優勢:簡化你的代碼,只訪問一次數據提升查詢性能。

我但願如今你已經可以很好理解grouping sets,若是你能在你的數據庫裏使用這個功能能夠在此留言,很是感謝!

感謝關注!

參考文章:

https://www.sqlpassion.at/archive/2014/09/15/the-power-of-grouping-sets-in-sql-server/

相關文章
相關標籤/搜索