九月 4, 2023
摘要:在本教程中,您将了解分组集合,以及如何使用 PostgreSQL 的GROUPING SETS
子句在查询中生成多个分组集。
目录
设置样例表
让我们开始创建一个新表,名为sales
,用于演示。
DROP TABLE IF EXISTS sales;
CREATE TABLE sales (
brand VARCHAR NOT NULL,
segment VARCHAR NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (brand, segment)
);
INSERT INTO sales (brand, segment, quantity)
VALUES
('ABC', 'Premium', 100),
('ABC', 'Basic', 200),
('XYZ', 'Premium', 100),
('XYZ', 'Basic', 300)
RETURNING *;
输出:
brand | segment | quantity
-------+---------+----------
ABC | Premium | 100
ABC | Basic | 200
XYZ | Premium | 100
XYZ | Basic | 300
(4 rows)
该sales
表存储按品牌和细分市场销售的产品数量。
GROUPING SETS 简介
分组集是一组列,您可以使用GROUP BY
子句对其进行分组。
分组集由放置在括号内的以逗号分隔的列列表表示:
(column1, column2, ...)
例如,以下查询使用GROUP BY
子句返回按品牌和细分市场销售的产品数量。换句话说,它定义了品牌和细分市场的分组集,表示为(brand, segement)
。
SELECT
brand,
segment,
SUM (quantity)
FROM
sales
GROUP BY
brand,
segment;
输出:
brand | segment | sum
-------+---------+-----
XYZ | Basic | 300
ABC | Premium | 100
ABC | Basic | 200
XYZ | Premium | 100
(4 rows)
以下查询查找某个品牌销售的产品数量。它定义了一个分组集(brand)
:
SELECT
brand,
SUM (quantity)
FROM
sales
GROUP BY
brand;
输出:
brand | sum
-------+-----
ABC | 300
XYZ | 400
(2 rows)
以下查询查找按细分市场销售的产品数量。它定义了一个分组集(segment)
:
SELECT
segment,
SUM (quantity)
FROM
sales
GROUP BY
segment;
输出:
segment | sum
---------+-----
Basic | 500
Premium | 200
(2 rows)
以下查询查找所有品牌和细分市场销售的产品数量。它定义了一个空分组集,用()
表示。
SELECT SUM (quantity) FROM sales;
输出:
sum
-----
700
(1 row)
假设您想要使用单个查询获取所有分组集。为了实现这一点,您可以使用UNION ALL
来组合上面的所有查询。
由于UNION ALL
要求所有结果集具有相同数量的具有兼容数据类型的列,因此您需要通过添加NULL
到每个结果集的SELECT
列表来调整查询,如下所示:
SELECT
brand,
segment,
SUM (quantity)
FROM
sales
GROUP BY
brand,
segment
UNION ALL
SELECT
brand,
NULL,
SUM (quantity)
FROM
sales
GROUP BY
brand
UNION ALL
SELECT
NULL,
segment,
SUM (quantity)
FROM
sales
GROUP BY
segment
UNION ALL
SELECT
NULL,
NULL,
SUM (quantity)
FROM
sales;
输出:
brand | segment | sum
-------+---------+-----
XYZ | Basic | 300
ABC | Premium | 100
ABC | Basic | 200
XYZ | Premium | 100
ABC | null | 300
XYZ | null | 400
null | Basic | 500
null | Premium | 200
null | null | 700
(9 rows)
此查询生成一个结果集,其中包含所有分组集的聚合。
尽管上面的查询按您的预期工作,但它有两个主要问题。
- 首先,它相当长。
- 其次,它存在性能问题,因为 PostgreSQL 必须为每个查询单独扫描
sales
表。
为了提高效率,PostgreSQL 提供了GROUPING SETS
子句,它是GROUP BY
子句的子子句。
GROUPING SETS
允许您在同一查询中定义多个分组集。
GROUPING SETS
的一般语法如下:
SELECT
c1,
c2,
aggregate_function(c3)
FROM
table_name
GROUP BY
GROUPING SETS (
(c1, c2),
(c1),
(c2),
()
);
在此语法中,我们有四个分组集:(c1,c2)
、(c1)
、(c2)
和()
。
要将此语法应用于上面的示例,您可以使用GROUPING SETS
子句而不是像UNION ALL
这样的子句:
SELECT
brand,
segment,
SUM (quantity)
FROM
sales
GROUP BY
GROUPING SETS (
(brand, segment),
(brand),
(segment),
()
);
输出:
brand | segment | sum
-------+---------+-----
null | null | 700
XYZ | Basic | 300
ABC | Premium | 100
ABC | Basic | 200
XYZ | Premium | 100
ABC | null | 300
XYZ | null | 400
null | Basic | 500
null | Premium | 200
(9 rows)
该查询更短且更具可读性。另外,PostgreSQL 会优化扫描sales
表的次数,不会多次扫描。
GROUPING 函数
GROUPING()
函数接受一个参数,该参数可以是列名或表达式:
GROUPING( column_name | expression)
column_name
或expression
必须与GROUP BY
子句中指定的列名或表达式匹配。
如果参数是当前分组集的成员,则GROUPING()
函数返回位 0,否则返回位 1 。
请参见以下示例:
SELECT
GROUPING(brand) grouping_brand,
GROUPING(segment) grouping_segment,
brand,
segment,
SUM (quantity)
FROM
sales
GROUP BY
GROUPING SETS (
(brand),
(segment),
()
)
ORDER BY
brand,
segment;
如图所示,当grouping_brand
的值为 0 时,sum
列会显示brand
的分组小计。
当grouping_segment
中的值为零时,sum
列显示segment
的分组小计。
您可以在HAVING
子句中使用GROUPING()
函数来查找每个品牌的小计,如下所示:
SELECT
GROUPING(brand) grouping_brand,
GROUPING(segment) grouping_segment,
brand,
segment,
SUM (quantity)
FROM
sales
GROUP BY
GROUPING SETS (
(brand),
(segment),
()
)
HAVING GROUPING(brand) = 0
ORDER BY
brand,
segment;
在本教程中,您学习了如何使用 PostgreSQL 的GROUPING SETS
生成多个分组集。