作者:Itzik Ben-Gan  翻译:张洪举

此文摘自作者的《Microsoft SQL Server 2012 T-SQL基础》。

分组集就是你据以分组的一个属性集。传统上,SQL中的单个聚合查询定义一个单个分组集。例如,下面的四个查询每个定义了一个单个分组集。

SELECT empid, custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY empid, custid;

SELECT empid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY empid;

SELECT custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY custid;

SELECT SUM(qty) AS sumqty

FROM dbo.Orders;

第一个查询定义了分组集(empid,custid),第二个是(empid),第三个是(custid),最后一个查询定义了空分组集()。此代码返回四个结果集,每个查询一个。

假设不是要四个单独的结果集,而是想要一个统一了四个分组集的所有聚合数据的单个结果集,可以使用UNION ALL集合运算符组合四个查询的结果集,实现此目标。由于集合运算符要求所有结果集需要具有相同列数的兼容架构,你需要调整查询,为缺失的列添加占位符(如NULL标记),类似于下面的代码。

SELECT empid, custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY empid, custid

UNION ALL

SELECT empid, NULL, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY empid

UNION ALL

SELECT NULL, custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY custid

UNION ALL

SELECT NULL, NULL, SUM(qty) AS sumqty

FROM dbo.Orders;

此代码生成了一个单个结果集,含有被统一的四个分组集的聚合数据。

empid custid sumqty
----------- --------- -----------
2 A 52
3 A 20
1 B 20
2 B 27
1 C 34
3 C 22
3 D 30
1 NULL 54
2 NULL 79
3 NULL 72
NULL A 72
NULL B 47
NULL C 56
NULL D 30
NULL NULL 205
(15 row(s) affected)

尽管你已经达到了目的,此解决方案具有两个主要问题——代码的长度和性能。此解决方案需要为每个分组集指定一个完整的GROUP BY查询,当有很多分组集时,查询可能会很长。此外,为处理查询,SQLServer将会为每个查询分别扫描源表,效率低下。

SQL Server支持几项遵循标准SQL的功能,能够在同一查询中定义多个分组集,包括GROUP BY子句的GROUPING SETSCUBEROLLUP从属子句,以及GROUPINGGROUPING_ID函数。

1. GROUPING SETS 从属子句

GROUPING SETS从属子句是一个对GROUP BY子句的强大增强,主要用于报表和数据仓库。通过使用此从属子句,可以在同一查询中定义多个分组集。只需列出你要定义的分组集,在GROUPING SETS从属子句的括号内以逗号分隔,并且每个分组集列出的成员在其括号内也要以逗号分隔。例如,下面的查询定义四个分组集:(empid, custid)(empid)(custid)()

SELECT empid, custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY

GROUPINGSETS

(

(empid,custid),

(empid),

(custid),

()

);

此查询在逻辑上等效于之前统一了四个聚合查询结果集的解决方案,返回相同的输出。不过,此查询相比之前的解决方案有两个主要优势——显然是它要求更少的代码,并且SQLServer会优化扫描源表的次数,而不是必须为每个分组集单独扫描源表。

2. CUBE从属子句

GROUP BY 子句的CUBE从属子句提供了一种定义多个分组集的简单方式。在CUBE从属子句的括号中,提供了一个以逗号分隔的成员列表后,会得到基于所定义的输入成员的所有可能的分组集。例如,CUBE(a, b, c)等效于GROUPING SETS( (a, b, c), (a, b), (a, c), (b, c), (a), (b), (c), ())。在集合理论中,能够从一个特定集合生成所有的元素子集的集合,称之为幂集。你可以认为CUBE子句就是分组集的幂集,它由给定的元素集合构成。

作为对之前查询中使用GROUPING SETS从属子句定义的四个分组集(empid, custid)(empid)(custid)()的替代,可以简单地使用CUBE(empid, custid)。下面是完整的查询。

SELECT empid, custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY CUBE(empid, custid);

3. ROLLUP从属子句

GROUP BY 子句的ROLLUP从属子句也是提供了一种定义多个分组集的简单方式。然而,与CUBE从属子句不同的是,ROLLUP不会基于输入成员生成能够被定义的所有可能分组集,而只是其中的一部分。ROLLUP假定输入成员之间是一个层次结构,并生成鉴于层次结构意义的所有分组集。换句话说,CUBE(a, b, c)根据三个输入成员生成了所有可能的八个分组集,而ROLLUP(a, b, c)仅生成四个分组集,其假定层次结构为a>b>c,等效于指定了GROUPING SETS( (a, b, c), (a, b), (a), () )

例如,假设要基于时间层次结构“订单年度>订单月份>订单日”,为所有能够定义的分组集返回订购数量总计,可以使用GROUPING SETS从属子句并显式地列出所有的四个可能分组集。

GROUPING SETS(

(YEAR(orderdate), MONTH(orderdate), DAY(orderdate)),

(YEAR(orderdate), MONTH(orderdate)),

(YEAR(orderdate)),

() )

此逻辑与使用更为经济的ROLLUP从属子句是等效的。

ROLLUP(YEAR(orderdate), MONTH(orderdate),DAY(orderdate))

下面是你需要运行的完整查询。

SELECT

YEAR(orderdate) AS orderyear,

MONTH(orderdate) AS ordermonth,

DAY(orderdate) AS orderday,

SUM(qty) ASsumqty

FROM dbo.Orders

GROUP BY ROLLUP(YEAR(orderdate), MONTH(orderdate),DAY(orderdate));

此查询会生成下面的输出。

orderyear ordermonth orderday sumqty
----------- -------------- ----------- -----------
2007 4 18 22
2007 4 NULL 22
2007 8 2 10
2007 8 NULL 10
2007 12 24 32
2007 12 NULL 32
2007 NULL NULL 64
2008 1 9 40
2008 1 18 14
2008 1 NULL 54
2008 2 12 12
2008 2 NULL 12
2008 NULL 2009 2 12 10
2009 2 16 20
2009 2 NULL 30
2009 4 18 15
2009 4 NULL 15
2009 9 7 30
2009 9 NULL 30
2009 NULL NULL 75
NULL NULL NULL 205NULL 66

4. GROUPING和GROUPING_ID函数

当有一个定义了多个分组集的单独查询时,你可能需要能够关联结果行与分组集,即确定与每个结果行相关联的分组集。只要所有分组元素定义为NOT NULL,这是很容易的。例如,请考虑下面的查询。

SELECT empid, custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY CUBE(empid, custid);

此查询会生成以下输出。

empid custid sumqty
----------- --------- -----------
2 A 52
3 A 20
NULL A 72
1 B 20
2 B 27
NULL B 47
1 C 34
3 C 22
NULL C 56
3 D 30
NULL D 30
NULL NULL 205
1 NULL 54
2 NULL 79
3 NULL 72

因为empidcustid列在dbo.Orders中定义为NOT NULL,这些列中的NULL仅代表一个占位符,指示该列没有参与当前的分组集。那么,例如,empid不为NULLcustid不为NULL的所有行是与分组集(empid, custid)相关联的,empid不为NULLcustidNULL的所有行是与分组集(empid)相关联的,依此类推。某些人以ALL或类似占位符覆盖NULL标志,就是要原始列不为空,这对报表是有帮助的。

但是,如果分组列在表中定义为允许NULL标记,你就不能确定结果集中的NULL是源自数据还是一个不参与分组集成员的占位符。一种能够判断与分组集相关联的确定方式(即使分组列允许NULL标记),是使用GROUPING函数。此函数接受一个列名称,如果该列是当前分组集的成员,返回0,否则返回1。

注意我觉得反常的是,GROUPING函数在元素不是分组集成员时返回1,在是的时候返回0。对我来说,在元素是分组集的成员时返回1(即true),不是时返回0,这样会更加清晰。不过,函数就是这么实施的,所以你只需确保已经意识到这一问题即可。

例如,下面的查询为每个分组元素调用了GROUPING函数。

SELECT

GROUPING(empid) AS grpemp,

GROUPING(custid) AS grpcust,  empid, custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY CUBE(empid, custid);

此查询返回下面的输出。

grpemp grpcust empid custid sumqty
--------- ---------- ----------- --------- -----------
0 0 2 A 52
0 0 3 A 20
1 0 NULL A 72
0 0 1 B 20
0 0 2 B 27
1 0 NULL B 47
0 0 1 C 34
0 0 3 C 22
1 0 NULL C 56
0 0 3 D 30
1 0 NULL D 30
1 1 NULL NULL 205
0 1 1 NULL 54
0 1 2 NULL 79
0 1 3 NULL 72

现在你再也不需要依靠NULL标记来判断结果行和分组集之间的相关性了。例如,grpemp为0且grpcust为0的所有行是与分组集(empid, custid)相关联的,grpemp为0且grpcust为1的所有行是与分组集(empid)相关联的,依此类推。

SQL Server支持另一个名为GROUPING_ID的函数,可以进一步简化结果行与分组集的关联处理。将参与任何分组集的所有元素作为函数的输入,例如GROUPING_ID(a, b, c, d),该函数返回一个整数位图,其中每位代表一个不同的输入元素,最右边的元素由最右边的位表示。例如,分组集(a, b, c, d)用整数0表示(即0×8+0×4+0×2+0×1),分组集(a, c)用整数5表示(即0×8+1×4+0×2+1×1),依此类推。

作为对之前查询中对每个分组元素调用GROUPING函数的替代,可以调用GROUPING_ID函数一次将所有分组元素提供给它作为输入,如下所示。

SELECT

GROUPING_ID(empid, custid) AS groupingset,   empid, custid, SUM(qty) AS sumqty

FROM dbo.Orders

GROUP BY CUBE(empid, custid);

此查询会生成下面的输出。

groupingset empid custid sumqty
-------------- ----------- --------- -----------
0 2 A 52
0 3 A 20
2 NULL A 72
0 1 B 20
0 2 B 27
2 NULL B 47
0 1 C 34
0 3 C 22
2 NULL C 56
0 3 D 30
2 NULL D 30
3 NULL NULL 205
1 1 NULL 54
1 2 NULL 79
1 3 NULL 72

现在你可以轻松地找出与分组集相关的每一行。整数0(二进制00)代表分组集
(empid, custid),整数1(二进制01)代表
(empid),整数2(二进制10)代表
(custid),整数3(二进制11)代表
()

在SQL Server 2012中如何使用分组集的更多相关文章

  1. (数据科学学习手册28)SQL server 2012中的查询语句汇总

    一.简介 数据库管理系统(DBMS)最重要的功能就是提供数据查询,即用户根据实际需求对数据进行筛选,并以特定形式进行显示.在Microsoft SQL Serve 2012 中,可以使用通用的SELE ...

  2. SQL Server 2012中Task是如何调度的?

    SQL Server 2012中Task是如何调度的?[原文来自:How It Works: SQL Server 2012 Database Engine Task Scheduling]     ...

  3. SQL Server 2012中快速插入批量数据的示例及疑惑

    SQL Server 2008中SQL应用系列--目录索引 今天在做一个案例演示时,在SQL Server 2012中使用Insert语句插入1万条数据,结果遇到了一个奇怪的现象,现将过程分享出来,以 ...

  4. 微软BI 之SSAS 系列 - 在SQL Server 2012 中开发 Analysis Services Multidimensional Project

    SQL Server 2012 中提供了开发 SSAS 项目的两种模型,一种是新增加的 Tabular Model 表格模型,另一种就是原始的 Multidimensional Model 多维模型. ...

  5. 在SQL Server 2012中实现CDC for Oracle

    在上篇在SSIS 2012中使用CDC(数据变更捕获)中,介绍了如何在SSIS 2012中使用CDC,本文在此基础上介绍,如何通过Attunity提供的Change Data Capture Desi ...

  6. 在SQL Server 2012中新建用户

    一.问题描述 在最开始装SQL Server 2012时我选择的是Windows身份认证方式,现在想添加一个用户采用SQL Server身份验证. 二.具体思路 1.新建用户 2.将新建的用户添加到相 ...

  7. SQL Server 2012中的AlwaysOn尝试

      简介 SQL Server2012中新增的AlwaysOn是一个新增高可用性解决方案.在AlwaysOn之前,SQL Server已经有的高可用性和数据恢复方案,比如数据库镜像,日志传送和故障转移 ...

  8. SQL SERVER技术内幕之8 分组集

    分组集就是分组(GROUP BY子句)使用的一组属性,在传统的SQL中,一个聚合查询只能定义一个分组集: 假设现在不想生成4个单独的结果集,而是希望生成一个统一的结果集,其中包含所有4个分组集的聚合 ...

  9. SQL Server 2012 中 Update FROM子句

    首先说明一下需求以及环境 创建Table1以及Table2两张表,并插入一下数据 USE AdventureWorks2012; GO IF OBJECT_ID ('dbo.Table1', 'U') ...

随机推荐

  1. 推送消息实现icon角标的动态显示

    在你自己服务器上做计数,客户端做减法并反馈给你的服务器 ,然后你服务器将需要显示的数字发送给苹果推送服务器(就是消息中的badge)比如:1,你服务器上发送出去3个推送消息到A手机           ...

  2. Linux 权限基础说明

      1 权限位说明 Linux文件或目录的权限位是由个9个权限位来控制的,每三位为一组,它们分别是文件属主(owner/user)读.写.执行,用户组(Group)的读.写.执行以及(Other)其他 ...

  3. JavaScript学习总结【3】、JS对象

    在 JS 中一切皆对象,并提供了多个内置对象,比如:String.Array.Date 等,此外还支持自定义对象.对象只是一种特殊类型的数据,并拥有属性和方法,属性是与对象相关的值,方法是能够在对象上 ...

  4. word2007在试图打开文件时遇到错误解决方法

    当您尝试在 Microsoft Office Word 2007 中打开 .docx 文件时,该文件打不开.此外,您还会收到以下错误消息: Word 在试图打开文件时遇到错误.请尝试下列方法:* 检查 ...

  5. 通过阅读ASP.NET MVC5 框架解密 路由的一点心得

    路由: 1.在ASP.NET中路由不专属与ASP.NET MVC,因为路由(Route)是在system.web 命名空间下的,所以传统的WebForm也可以使用路由. 2.什么叫做路由 采用某种机制 ...

  6. 关于PowerDesigner

    1. PowerDesigner将所有的小写改为大写:Tools->Model Option->左侧菜单中“Naming conversion”->Column->Code – ...

  7. NSURLSession -- 实际开发中运用

    NSURLSession实际请求 iOS9使用http请求方法: 在工程info.plist文件内添加NSAppTransportSecurity键,类型为dictionary 在NSAppTrans ...

  8. 如何通过友盟分析发布后App崩溃日志-b

    要分析崩溃日志,首先需要保留发布时的编译出来的.xcarchive文件.这个文件包含了.DSYM文件. 我一般的做法是,发布成功后,把这个文件.xcarchive直接提交到代码版本库对应的版本分支里, ...

  9. Delphi中快捷键的使用

    CTRL+SHIFT+↑(↓) 在过程.函数.事件内部, 可跳跃到相应的过程.函数.事件的定义(在INTERFACE和IMPLEMENTATION之间来回切换)CTRL+J (弹出DELPHI语句提示 ...

  10. bzoj 1045: [HAOI2008] 糖果传递 贪心

    1045: [HAOI2008] 糖果传递 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1812  Solved: 846[Submit][Stat ...