结构化查询语言/汇总立方体
在 分组 一章中,我们已经看到,关键字 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) 和 () |