当前位置:首页 > 问答 > 正文

微软SQL Server里用汇总技术搞数据分析,mssql rollup怎么实现的那些事儿

微软SQL Server里用汇总技术搞数据分析,特别是那个叫ROLLUP的操作符,其实就是在GROUP BY子句上玩出的一个高级花样,它最厉害的地方在于,能一口气帮你生成多级的小计和总计,让数据的不同层级汇总结果一次性全出来,不用你再分好几次写查询。

ROLLUP到底是干啥的?一个简单的比方

想象一下,你是个超市经理,想看销售数据,你不仅想知道每个部门(比如生鲜部、日用品部)的销售额,还想知道每个部门下每个品类(比如生鲜部下的水果、蔬菜)的销售额,最后你还想知道整个超市所有部门加起来的销售总额。

笨办法是什么呢?是你先写个查询,按部门和品类分组,算出各部门下各品类的销售额,然后再写一个查询,只按部门分组,算出各部门的小计,最后再写一个查询,啥分组条件都不要,算出总计,你得跑三次查询,再把结果拼到一起看。

而ROLLUP这个工具,就像个智能助手,你只需要告诉它“你先按部门分,再按品类分,然后帮我层层汇总”,它就能在一张结果表里,把从最细的粒度(部门+品类)到中间粒度(部门小计)再到最粗粒度(全超市总计)的所有结果都给你算出来,摆在一起,这就是ROLLUP的核心价值:多维度层级汇总

ROLLUP在SQL Server里怎么用?语法长啥样?

它的语法不复杂,是附在GROUP BY子句后面的,根据微软官方文档(来源:Microsoft Learn - SELECT - GROUP BY (Transact-SQL)),基本写法是这样的:

微软SQL Server里用汇总技术搞数据分析,mssql rollup怎么实现的那些事儿

SELECT 列1, 列2, ..., 聚合函数(列N)
FROM 表名
GROUP BY ROLLUP (列1, 列2, ...);

你也可以用另一种更老一点的语法,但效果一样:

SELECT 列1, 列2, ..., 聚合函数(列N)
FROM 表名
GROUP BY 列1, 列2, ... WITH ROLLUP;

不过微软建议用第一种,也就是GROUP BY ROLLUP (...)这种写法,因为更符合SQL标准,也更清晰。

一个活生生的例子,一看就懂

光说没用,我们用一个假设的销售表Sales来演示,这个表有Year(年份)、Quarter(季度)、Region(区域)和SalesAmount(销售额)这几个字段。

现在我们想分析:按年份、季度、区域来汇总销售额,并且要看到每年每季度的小计、每年的小计、以及所有年份的总计。

微软SQL Server里用汇总技术搞数据分析,mssql rollup怎么实现的那些事儿

用ROLLUP的查询就是这样写的:

SELECT
    Year,
    Quarter,
    Region,
    SUM(SalesAmount) AS TotalSales
FROM Sales
GROUP BY ROLLUP (Year, Quarter, Region);

这个查询跑出来的结果会是什么样呢?它会是一个包含了很多行的结果集,里面不仅有详细数据,还有各种小计和总计行,关键在于,在小计或总计的那一行,被汇总掉的哪个列,它的值会显示为NULL

举个例子,可能的结果会像下面这样:

Year Quarter Region TotalSales
2023 Q1 North 10000
2023 Q1 South 12000
2023 Q1 NULL 22000
2023 Q2 East 15000
2023 Q2 West 11000
2023 Q2 NULL 26000
2023 NULL NULL 48000
2024 Q1 North 13000
... ... ... ...
NULL NULL NULL 100000

你看,通过这个结果,你可以清晰地看到:

  • 最细的明细:2023年Q1北部卖了10000。
  • 中间层级的小计:2023年Q1总共卖了22000(这一行Quarter有值,Region是NULL)。
  • 更高层级的小计:2023年全年卖了48000(这一行Year有值,Quarter和Region是NULL)。
  • 最终的总计:所有年份加起来卖了100000(这一行Year, Quarter, Region全是NULL)。

怎么区分小计行和真正的NULL值?GROUPING()函数来帮忙

微软SQL Server里用汇总技术搞数据分析,mssql rollup怎么实现的那些事儿

你原始数据里可能本来就有NULL值,比如某个区域可能没录入名字,本身就是NULL,这就容易跟ROLLUP生成的小计行里的NULL搞混,为了解决这个问题,SQL Server提供了一个叫GROUPING()的函数(来源:Microsoft Learn - GROUPING (Transact-SQL))。

这个函数非常聪明,它会告诉你某个列上的NULL是不是因为ROLLUP(或CUBE)汇总产生的,如果是汇总产生的,它就返回1;如果是数据里自带的NULL,它就返回0。

我们可以改进一下查询,让结果更清晰:

SELECT
    Year,
    Quarter,
    Region,
    SUM(SalesAmount) AS TotalSales,
    GROUPING(Year) AS IsTotalYear,       -- 如果是全年总计,此列为1
    GROUPING(Quarter) AS IsTotalQuarter, -- 如果是季度小计,此列为1
    GROUPING(Region) AS IsTotalRegion    -- 如果是区域小计,此列为1
FROM Sales
GROUP BY ROLLUP (Year, Quarter, Region);

这样,输出结果里就会多出三列标志位,当你看到一行数据里Year是NULL,同时IsTotalYear是1,你就百分百确定这一行是ROLLUP生成的全年汇总行,而不是数据缺失。

小结一下

在微软SQL Server里玩转ROLLUP,就是那么几步事:

  1. 目的明确:你想要生成带有多层级小计和总计的报告。
  2. 语法记住:在GROUP BY后面加上ROLLUP (列A, 列B, ...),列的顺序很重要,它决定了汇总的层级关系,是从左到右一层层汇总上去的。
  3. 结果识别:汇总产生的行,对应列的值会是NULL。
  4. 高级技巧:用GROUPING()函数来准确区分汇总NULL和真实NULL,让数据分析更精准。

掌握了ROLLUP,你写汇总查询的效率会大大提高,不用再吭哧吭哧地写一堆UNION ALL把多个查询结果拼起来了,这对于做财务报表、销售分析、库存统计等各种需要层层钻取看数据的场景,尤其好用。