文章目录

小计、合计与总计
        多维度交叉统计
        自定义统计维度
        数据透视表
        总结

学习过 SQL 的人都知道,使用聚合函数(AVG、SUM、COUNT、MIN/MAX 等)和分组操作(GROUP BY)可以对数据进行基本的统计分析,例如统计公司员工的人数、每个部门的平均月薪等。如果想要回顾这些基础概念,可以参考这篇文章。

不过 SQL 不仅仅能够进行这些基本的分组汇总,它还提供了许多高级的统计分析功能。本文就来介绍一下如何使用 SQL 实现销售数据的小计、合计、总计以及多维度交叉统计和数据透视表。我们首先列出这些功能在主流数据库中的支持情况:

SQL SERVER 统计各渠道2023年各季度的销售数量 sql计算每月的销售总额_SQL

  • 参考下文中的具体讨论。本文使用的示例数据可以点此下载,如果没有特殊说明,以下示例适用于上面的 5 种数据库。
    小计、合计与总计

我们先查看一下示例表中的数据:

select * from sales_data;

saledate

product

channel

amount

2019-01-01

桔子

淘宝

1864.00

2019-01-01

桔子

京东

1329.00

2019-01-01

桔子

店面

1736.00

2019-01-01

香蕉

淘宝

1573.00

2019-01-01

香蕉

京东

1364.00

2019-01-01

香蕉

店面

1178.00

2019-01-01

苹果

淘宝

511.00

2019-01-01

苹果

京东

568.00

2019-01-01

苹果

店面

847.00

这是一个模拟的销售数据,记录了不同日期(2019-01-01 到 2019-06-30)、三种不同产品、三种不同渠道的销量情况。

以下语句使用GROUP BY统计了三种不同产品各自的总销量:

select product, sum(amount)
from sales_data
group by product;

product

sum(amount)

桔子

909261.00

香蕉

925369.00

苹果

937052.00

显然,还可以编写 SQL 语句统计三种不同产品在不同渠道各自的销量合计、所以产品的销量总计等。但是如何一次获取这些按照不同维度进行统计的结果呢?我们可以使用GROUP BY的第一个扩展选项:ROLLUP。例如:

– Oracle、SQL Server、PostgreSQL
select product “产品”, channel “渠道”, sum(amount) “销量”
from sales_data
group by rollup (product, channel);

产品

渠道

销量

桔子

店面

294680.00

桔子

京东

311799.00

桔子

淘宝

302782.00

桔子

NULL

909261.00

苹果

店面

306643.00

苹果

京东

318614.00

苹果

淘宝

311795.00

苹果

NULL

937052.00

香蕉

店面

311445.00

香蕉

京东

306033.00

香蕉

淘宝

307891.00

香蕉

NULL

925369.00

NULL

NULL

2771682.00

其中,ROLLUP表示先按照 (product, channel) 的组合计算不同产品、不同渠道的销量小计,然后按照计算不同产品(product)、所有渠道的销量和计(结果中的 channel 字段显示为 NULL),最后计算所有产品、所有渠道的销量总计(结果中的 product 和 channel 字段都为 NULL)。

如果使用 MySQL 数据库,ROLLUP的使用略有不同:

– MySQL
 select product, channel, sum(amount)
 from sales_data
 group by product, channel with rollup;MySQL 在分组字段之后使用with rollup选项,查询的结果与其他数据库相同。
ROLLUP选项可以使用UNION合并多个查询结果进行模拟:
with d as (
 select product, channel , sum(amount) amount
 from sales_data
 group by product, channel
 )
 select product “产品”, channel “渠道”, amount “销量” from d
 union all
 select product, NULL, sum(amount) from d group by product
 union all
 select NULL, NULL, sum(amount) from d;

其中,WITH表示定义通用表表达式,类似于临时表;关于通用表表达式的概念可以参考这篇文章。以上语句正好解释了ROLLUP选项的作用。

📝GROUP BY子句的ROLLUP选项是一种按照层次从下往上依次汇总的过程,需要汇总 N + 1 个维度,N 是分组字段的个数。

多维度交叉统计

如果我们的销量报表需要统计以下信息:

不同产品、不同渠道的销量小计;
不同产品、所有渠道的销量合计;
所有产品、不同渠道的销量合计;
所有产品、所有渠道的销量总计。

由于ROLLUP选项是按照分组字段的顺序依次往上汇聚,(product, channel) 无法获取所有产品、不同渠道的销量合计,(channel, product) 又无法获取不同产品、所有渠道的销量合计。虽然可以查询两次然后去除重复结果,但是不方便;况且当我们的分组字段增加到 3 个或者 4 个时,组合情况更多。

为此,我们可以使用GROUP BY的第二个扩展选项:CUBE。以下语句可以实现上面的统计需求:

– Oracle、SQL Server、PostgreSQL
 select coalesce(product, ‘【全部产品】’) “产品”, coalesce(channel, ‘【所有渠道】’) “渠道”, sum(amount) “销量”
 from sales_data
 group by cube (product,channel)
 order by product, channel;

产品

渠道

销量

桔子

京东

311799

桔子

店面

294680

桔子

淘宝

302782

桔子

【所有渠道】

909261

苹果

京东

318614

苹果

店面

306643

苹果

淘宝

311795

苹果

【所有渠道】

937052

香蕉

京东

306033

香蕉

店面

311445

香蕉

淘宝

307891

香蕉

【所有渠道】

925369

【全部产品】

京东

936446

【全部产品】

店面

912768

【全部产品】

淘宝

922468

【全部产品】

【所有渠道】

2771682

为了更好地查看结果,我们使用 coalesce 函数对 NULL 进行了转换显示;CUBE基于分组字段的任意组合进行汇总,比ROLLUP获得的更多维度的统计信息。

MySQL 目前没有提供CUBE选项。以上示例中的分组字段只有 2 个,可以使用UNION合并多个查询结果进行模拟:

with d as (
 select product, channel , sum(amount) amount
 from sales_data
 group by product, channel
 )
 select product “产品”, channel “渠道”, amount “销量” from d
 union all
 select product, NULL, sum(amount) from d group by product
 union all
 select NULL, channel, sum(amount) from d group by channel
 union all
 select NULL, NULL, sum(amount) from d;

这种方法正好解释了CUBE选项的作用,也适用于其他数据库;但是如果分组字段达到 3 个以上就比较麻烦。