跳转至内容

结构化查询语言/汇总立方体

来自维基教科书,开放的书,为开放的世界

分组 一章中,我们已经看到,关键字 GROUP BY 在结果集中创建了行的分组。此外,聚合函数SUM() 计算这些组中每个组的压缩值。

由于 GROUP BY 可以按多个列进行汇总,因此通常需要计算“超级组”的汇总值,这些“超级组”是通过从 GROUP BY 规范中依次省略一列而产生的。

示例表

[编辑 | 编辑源代码]

为了说明这种情况,我们提供一个示例表和针对此类表的典型问题。

CREATE TABLE car_pool (
  -- define columns (name / type / default value / nullable)
  id           DECIMAL      NOT NULL,
  producer     VARCHAR(50)  NOT NULL,
  model        VARCHAR(50)  NOT NULL,
  yyyy         DECIMAL      NOT NULL CHECK (yyyy BETWEEN 1970 AND 2020),
  counter      DECIMAL      NOT NULL CHECK (counter >= 0),
  CONSTRAINT   car_pool_pk PRIMARY KEY (id)
);
--
INSERT INTO car_pool VALUES ( 1, 'VW',     'Golf',    2005, 5);
INSERT INTO car_pool VALUES ( 2, 'VW',     'Golf',    2006, 2);
INSERT INTO car_pool VALUES ( 3, 'VW',     'Golf',    2007, 3);
INSERT INTO car_pool VALUES ( 4, 'VW',     'Golf',    2008, 3);
INSERT INTO car_pool VALUES ( 5, 'VW',     'Passat',  2005, 5);
INSERT INTO car_pool VALUES ( 6, 'VW',     'Passat',  2006, 1);
INSERT INTO car_pool VALUES ( 7, 'VW',     'Beetle',  2005, 1);
INSERT INTO car_pool VALUES ( 8, 'VW',     'Beetle',  2006, 2);
INSERT INTO car_pool VALUES ( 9, 'VW',     'Beetle',  2008, 4);
INSERT INTO car_pool VALUES (10, 'Toyota', 'Corolla', 2005, 4);
INSERT INTO car_pool VALUES (11, 'Toyota', 'Corolla', 2006, 3);
INSERT INTO car_pool VALUES (12, 'Toyota', 'Corolla', 2007, 2);
INSERT INTO car_pool VALUES (13, 'Toyota', 'Corolla', 2008, 4);
INSERT INTO car_pool VALUES (14, 'Toyota', 'Prius',   2005, 1);
INSERT INTO car_pool VALUES (15, 'Toyota', 'Prius',   2006, 1);
INSERT INTO car_pool VALUES (16, 'Toyota', 'Hilux',   2005, 1);
INSERT INTO car_pool VALUES (17, 'Toyota', 'Hilux',   2006, 1);
INSERT INTO car_pool VALUES (18, 'Toyota', 'Hilux',   2008, 1);
--
COMMIT;

在该表中,有两个不同的汽车生产商、6 个型号和 4 年。针对此类表的典型问题是

  • 每个生产商或每个型号的汽车数量。
  • 一些标准组合的汽车数量,例如:生产商加型号或生产商加年份。
  • 汽车总数(不含任何标准)。

正如我们所,关键字 GROUP BY 为完全一个分组级别提供压缩数据,在本例中为生产商型号

SELECT producer, model, sum(counter) as cnt
FROM   car_pool
GROUP BY producer, model
ORDER BY producer, cnt desc;
--
Toyota	Corolla	13
Toyota	Hilux	3
Toyota	Prius	2
VW	Golf	13
VW	Beetle	7
VW	Passat	6

在这种情况下,人们还希望知道更高分组的相应值:每个生产商或整个表的值。这可以通过提交略微不同的 SELECT 来实现。

SELECT producer, sum(counter) as cnt
FROM   car_pool
GROUP BY producer
ORDER BY producer, cnt desc;
--
Toyota	18
VW	26
--
--
SELECT sum(counter) as cnt
FROM   car_pool;
--
44

原则上,可以使用 UNION 组合这些 SELECT,或者依次提交它们。但由于这是一个标准要求,SQL 提供了一个更优雅的解决方案,即用 ROLLUP 关键字扩展 GROUP BY。基于 GROUP BY 的结果,它为每个上级组提供额外的行,这些上级组是通过依次省略分组标准而产生的。

SELECT producer, model, sum(counter) as cnt
FROM   car_pool
GROUP BY ROLLUP (producer, model);  -- the MySQL syntax is: GROUP BY producer, model WITH ROLLUP
--
Toyota	Corolla	13
Toyota	Hilux	3
Toyota	Prius	2
Toyota		18   <-- the additional row per first producer
VW	Beetle	7
VW	Golf	13
VW	Passat	6
VW		26   <-- the additional row per next producer
		44   <-- the additional row per all producers

简单的 GROUP BY 语句在生产商型号级别创建行。ROLLUP 关键字会导致创建额外的行,其中首先省略型号,然后省略型号生产商

ROLLUP 关键字提供了层次结构视图适合的解决方案。但在数据仓库应用程序中,人们希望自由地在聚合数据中导航,而不仅仅是从上到下。为了支持此要求,SQL 标准提供了关键字 CUBE。它是 ROLLUP 的扩展,它为GROUP BY 列的所有可能组合提供额外的行。

在我们的上述示例中,有两个列生产商型号,ROLLUP 为“生产商-only”和“no criteria”(= 完整表)创建了行。除此之外,CUBE 为“型号-only”创建了行。(如果不同的生产商使用相同的型号名称,则此类行将只导致 1 个额外的行。)

SELECT producer, model, SUM(counter) AS cnt
FROM   car_pool
GROUP BY CUBE (producer, model);  -- not supported by MySQL
--
Toyota	Corolla	13
Toyota	Hilux	3
Toyota	Prius	2
Toyota	- 	18
VW	Beetle	7
VW	Golf	13
VW	Passat	6
VW	- 	26
- 	Beetle	7        <--
- 	Corolla	13       <--
- 	Golf	13       <-- additional rows for 'model-only'
- 	Hilux	3        <--
- 	Passat	6        <--
- 	Prius	2        <--
- 	- 	44

如果存在三个分组列 c1、c2 和 c3,则这些关键字会导致以下分组。

GROUP BY (c1, c2, c3)
GROUP BY ROLLUP (c1, c2, c3), (c1, c2), (c1) 和 ()
GROUP BY CUBE (c1, c2, c3), (c1, c2), (c1, c3), (c2, c3), (c1), (c2), (c3) 和 ()


华夏公益教科书