Grouping Sets、Rollup和Cube擴(kuò)展
在關(guān)系型數(shù)據(jù)庫中,通常需要使用多個SELECT + UNION
語句來實現(xiàn)按照多組維度的結(jié)果分組,PolarDB-X 1.0新增支持通過Grouping Sets、Rollup和Cube擴(kuò)展來實現(xiàn)這一目的。此外,PolarDB-X 1.0還支持在SELECT命令或HAVING子句中使用GROUPING函數(shù)和GROUPING_ID函數(shù),來幫助解釋使用上述擴(kuò)展時的結(jié)果。本文將介紹相關(guān)語法和示例。
前提條件
PolarDB-X 1.0實例版本需為5.4.10及以上。關(guān)于如何查看實例版本,請參見查看實例版本。
注意事項
本文介紹的所有GROUP BY相關(guān)的擴(kuò)展語法,均不支持查詢下推至
LogicalView
算子中執(zhí)行。關(guān)于查詢下推,請參見查詢改寫與下推。本文示例中所用測試數(shù)據(jù)信息如下:
使用如下語句創(chuàng)建一張
requests
表:CREATE TABLE requests ( `id` int(10) UNSIGNED NOT NULL, `os` varchar(20) DEFAULT NULL, `device` varchar(20) DEFAULT NULL, `city` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8 dbpartition BY hash(`id`) tbpartition BY hash(`id`);
在
requests
表中使用如下語句插入測試所需的數(shù)據(jù):INSERT INTO requests (id, os, device, city) VALUES (1, 'windows', 'PC', 'Beijing'), (2, 'windows', 'PC', 'Shijiazhuang'), (3, 'linux', 'Phone', 'Beijing'), (4, 'windows', 'PC', 'Beijing'), (5, 'ios', 'Phone', 'Shijiazhuang'), (6, 'linux', 'PC', 'Beijing'), (7, 'windows', 'Phone', 'Shijiazhuang');
GROUPING SETS擴(kuò)展
功能介紹
GROUPING SETS是GROUP BY子句的擴(kuò)展,可以生成一個結(jié)果集,該結(jié)果集實際上是基于不同分組的多個結(jié)果集的串聯(lián)(與UNION ALL運(yùn)算結(jié)果類似),但UNION ALL運(yùn)算和GROUPING SETS擴(kuò)展并不會消除合并結(jié)果集中的重復(fù)行。
語法
GROUPING SETS ( { expr_1 | ( expr_1a [, expr_1b ] ...) | ROLLUP ( expr_list ) | CUBE ( expr_list ) } [, ...] )
說明GROUPING SETS擴(kuò)展可包含一個或多個由半角逗號(,)分隔表達(dá)式(如
expr_1
或(expr_1a [, expr_1b ] ...)
)的任意組合,以及帶半角圓括號(())的表達(dá)式列表(如( expr_list )
),其中:每個表達(dá)式都可用于確定結(jié)果集的分組方式。
GROUPING SETS內(nèi)也支持嵌套使用ROLLUP或者CUBE。
示例
通過GROUPING SETS擴(kuò)展對數(shù)據(jù)進(jìn)行分組查詢,語法如下:
select os,device, city ,count(*) from requests group by grouping sets((os, device), (city), ()); 上述語句等效于如下語句: select os, device, NULL, count(*) from requests group by os, device union all select NULL, NULL, NULL, count(*) from requests union all select null, null, city, count(*) from requests group by city;
返回結(jié)果如下:
+---------+--------+--------------+----------+ | os | device | city | count(*) | +---------+--------+--------------+----------+ | windows | PC | NULL | 3 | | linux | PC | NULL | 1 | | linux | Phone | NULL | 1 | | windows | Phone | NULL | 1 | | ios | Phone | NULL | 1 | | NULL | NULL | Shijiazhuang | 3 | | NULL | NULL | Beijing | 4 | | NULL | NULL | NULL | 7 | +---------+--------+--------------+----------+
說明未在分組集中使用的表達(dá)式,會用NULL充當(dāng)占位符,便于對這些未在分組集使用的結(jié)果集進(jìn)行操作,例如結(jié)果
city
列中顯示為NULL的行。通過在GROUPING SETS中嵌套ROLLUP來對數(shù)據(jù)進(jìn)行分組,語法如下:
select os,device, city ,count(*) from requests group by grouping sets((city), ROLLUP(os, device)); 上述語句等效于如下語句: select os,device, city ,count(*) from requests group by grouping sets((city), (os), (os, device), ());
返回結(jié)果如下:
+---------+--------+--------------+----------+ | os | device | city | count(*) | +---------+--------+--------------+----------+ | NULL | NULL | Shijiazhuang | 3 | | NULL | NULL | Beijing | 4 | | windows | PC | NULL | 3 | | linux | PC | NULL | 1 | | ios | Phone | NULL | 1 | | linux | Phone | NULL | 1 | | windows | Phone | NULL | 1 | | windows | NULL | NULL | 4 | | linux | NULL | NULL | 2 | | ios | NULL | NULL | 1 | | NULL | NULL | NULL | 7 | +---------+--------+--------------+----------+
通過在GROUPING SETS中嵌套CUBE擴(kuò)展來對數(shù)據(jù)進(jìn)行分組,語法如下:
select os,device, city ,count(*) from requests group by grouping sets((city), CUBE(os, device)); 上述語句等效于如下語句: select os,device, city ,count(*) from requests group by grouping sets((city), (os), (os, device), (), (device));
返回結(jié)果如下:
+---------+--------+--------------+----------+ | os | device | city | count(*) | +---------+--------+--------------+----------+ | NULL | NULL | Beijing | 4 | | NULL | NULL | Shijiazhuang | 3 | | windows | PC | NULL | 3 | | ios | Phone | NULL | 1 | | linux | Phone | NULL | 1 | | windows | Phone | NULL | 1 | | linux | PC | NULL | 1 | | windows | NULL | NULL | 4 | | ios | NULL | NULL | 1 | | linux | NULL | NULL | 2 | | NULL | PC | NULL | 4 | | NULL | Phone | NULL | 3 | | NULL | NULL | NULL | 7 | +---------+--------+--------------+----------+
通過GROUP BY、CUBE和GROUPING SETS組合產(chǎn)生GROUPING SETS,示例如下:
select os,device, city, count(*) from requests group by os, cube(os,device), grouping sets(city); 上述語句等效于如下語句: select os,device, city, count(*) from requests group by grouping sets((os,device,city),(os,city);
返回結(jié)果如下:
+---------+--------+--------------+----------+ | os | device | city | count(*) | +---------+--------+--------------+----------+ | linux | Phone | Beijing | 1 | | windows | Phone | Shijiazhuang | 1 | | windows | PC | Shijiazhuang | 1 | | linux | PC | Beijing | 1 | | windows | PC | Beijing | 2 | | ios | Phone | Shijiazhuang | 1 | | linux | NULL | Beijing | 2 | | windows | NULL | Shijiazhuang | 2 | | windows | NULL | Beijing | 2 | | ios | NULL | Shijiazhuang | 1 | +---------+--------+--------------+----------+
ROLLUP擴(kuò)展
功能介紹
ROLLUP擴(kuò)展生成一系列有總計的分層組,每個分層組都有小計。該層次結(jié)構(gòu)的順序由ROLLUP表達(dá)式列表中給定的表達(dá)式的順序確定。該層次結(jié)構(gòu)的頂部是列表中最左側(cè)的項。每個連續(xù)項都會沿右側(cè)在該層次結(jié)構(gòu)中向下移動,最右側(cè)的項是最低級別。
語法
ROLLUP ( { expr_1 | ( expr_1a [, expr_1b ] ...) } [, expr_2 | ( expr_2a [, expr_2b ] ...) ] ...)
說明每個表達(dá)式都會用于確定結(jié)果集的分組方式。如果采用帶圓括號形式的表達(dá)式,例如
( expr_1a, expr_1b, ...)
,則expr_1a
和expr_1b
返回的值組合定義層次結(jié)構(gòu)的單個分組級別。對于列表中的第一項,例如
expr_1
或( expr_1a, expr_1b, ...)
的組合,PolarDB-X 1.0將為每個唯一值返回一個小計。對于列表中的第二項,例如expr_2
或( expr_2a, expr_2b, ...)
的組合,PolarDB-X 1.0將為第二項的每個分組中的每個唯一值返回一個小計,依此類推。最后,PolarDB-X 1.0將為整個結(jié)果集返回一個總計。對于小計行,將為小計包含的各項返回NULL。
示例
通過ROLLUP對
(os, device, city)
按層級聚合的方式產(chǎn)生GROUPING SETS,語法如下:select os,device, city, count(*) from requests group by rollup (os, device, city); 上述語句等效于如下語句: select os,device, city, count(*) from requests group by os, device, city with rollup; 也等效于如下語句: select os,device, city, count(*) from requests group by grouping sets ((os, device, city),(os, device),(os),());
返回結(jié)果如下:
+---------+--------+--------------+----------+ | os | device | city | count(*) | +---------+--------+--------------+----------+ | windows | PC | Beijing | 2 | | ios | Phone | Shijiazhuang | 1 | | windows | PC | Shijiazhuang | 1 | | linux | PC | Beijing | 1 | | linux | Phone | Beijing | 1 | | windows | Phone | Shijiazhuang | 1 | | windows | PC | NULL | 3 | | ios | Phone | NULL | 1 | | linux | PC | NULL | 1 | | linux | Phone | NULL | 1 | | windows | Phone | NULL | 1 | | windows | NULL | NULL | 4 | | ios | NULL | NULL | 1 | | linux | NULL | NULL | 2 | | NULL | NULL | NULL | 7 | +---------+--------+--------------+----------+
通過ROLLUP對
os, (os,device), city
按層級聚合的方式產(chǎn)生GROUPING SETS,語法如下:select os,device, city, count(*) from requests group by rollup (os, (os,device), city); 上述語句等效于如下語句: select os,device, city, count(*) from requests group by os, (os,device), city with rollup; 也等效于如下語句: select os,device, city, count(*) from requests group by grouping sets ((os, device, city),(os, device),(os),());
返回結(jié)果如下:
+---------+--------+--------------+----------+ | os | device | city | count(*) | +---------+--------+--------------+----------+ | windows | PC | Beijing | 2 | | windows | PC | Shijiazhuang | 1 | | linux | PC | Beijing | 1 | | linux | Phone | Beijing | 1 | | windows | Phone | Shijiazhuang | 1 | | ios | Phone | Shijiazhuang | 1 | | windows | PC | NULL | 3 | | linux | PC | NULL | 1 | | linux | Phone | NULL | 1 | | windows | Phone | NULL | 1 | | ios | Phone | NULL | 1 | | windows | NULL | NULL | 4 | | linux | NULL | NULL | 2 | | ios | NULL | NULL | 1 | | NULL | NULL | NULL | 7 | +---------+--------+--------------+----------+
CUBE擴(kuò)展
功能介紹
CUBE擴(kuò)展與ROLLUP擴(kuò)展類似,但與生成分組并基于ROLLUP表達(dá)式列表中從左到右的項列表生成層次結(jié)構(gòu)的ROLLUP擴(kuò)展不同,CUBE是基于CUBE表達(dá)式列表中所有項的每個排列生成分組和小計。因此,與對同一表達(dá)式列表執(zhí)行的ROLLUP相比,CUBE結(jié)果集會包含更多的行。
語法
CUBE ( { expr_1 | ( expr_1a [, expr_1b ] ...) } [, expr_2 | ( expr_2a [, expr_2b ] ...) ] ...)
說明每個表達(dá)式都會用于確定結(jié)果集的分組方式。如果采用帶半角圓括號的形式,例如
( expr_1a, expr_1b, ...)
,則expr_1a
和expr_1b
返回的值組合定義單個組。對于列表中的第一項,例如
expr_1
或( expr_1a, expr_1b, ...)
的組合,PolarDB-X 1.0將為每個唯一值返回一個小計。對于列表中的第二項,例如expr_2
或( expr_2a, expr_2b, ...)
的組合,PolarDB-X 1.0在為每個唯一值返回一個小計的同時,還將為第一項和第二項的每個唯一組合返回一個小計。如果存在第三項,PolarDB-X 1.0則會為第三項的每個唯一值、第三項和第一項組合的每個唯一值、第三項和第二項組合的每個唯一值以及第三項、第二項和第一項組合的每個唯一值返回一個小計。最后,再將為整個結(jié)果集返回一個總計。對于小計行,將為小計包含的各項返回NULL。
示例
通過CUBE枚舉
(os, device, city)
的所有可能列為GROUPING SETS,語法如下:select os,device, city, count(*) from requests group by cube (os, device, city); 上述語句等效于如下語句: select os,device, city, count(*) from requests group by grouping sets ((os, device, city),(os, device),(os, city),(device,city),(os),(device),(city),());
返回結(jié)果如下:
+---------+--------+--------------+----------+ | os | device | city | count(*) | +---------+--------+--------------+----------+ | linux | Phone | Beijing | 1 | | windows | Phone | Shijiazhuang | 1 | | windows | PC | Beijing | 2 | | ios | Phone | Shijiazhuang | 1 | | windows | PC | Shijiazhuang | 1 | | linux | PC | Beijing | 1 | | linux | Phone | NULL | 1 | | windows | Phone | NULL | 1 | | windows | PC | NULL | 3 | | ios | Phone | NULL | 1 | | linux | PC | NULL | 1 | | linux | NULL | Beijing | 2 | | windows | NULL | Shijiazhuang | 2 | | windows | NULL | Beijing | 2 | | ios | NULL | Shijiazhuang | 1 | | linux | NULL | NULL | 2 | | windows | NULL | NULL | 4 | | ios | NULL | NULL | 1 | | NULL | Phone | Beijing | 1 | | NULL | Phone | Shijiazhuang | 2 | | NULL | PC | Beijing | 3 | | NULL | PC | Shijiazhuang | 1 | | NULL | Phone | NULL | 3 | | NULL | PC | NULL | 4 | | NULL | NULL | Beijing | 4 | | NULL | NULL | Shijiazhuang | 3 | | NULL | NULL | NULL | 7 | +---------+--------+--------------+----------+
通過CUBE枚舉
(os, device),(device, city)
所有可能列為GROUPING SETS,語法如下:select os,device, city, count(*) from requests group by cube ((os, device), (device, city)); 上述語句等效于如下語句: select os,device, city, count(*) from requests group by grouping sets ((os, device, city),(os, device),(device,city),());
返回結(jié)果如下:
+---------+--------+--------------+----------+ | os | device | city | count(*) | +---------+--------+--------------+----------+ | linux | Phone | Beijing | 1 | | windows | Phone | Shijiazhuang | 1 | | windows | PC | Beijing | 2 | | windows | PC | Shijiazhuang | 1 | | linux | PC | Beijing | 1 | | ios | Phone | Shijiazhuang | 1 | | linux | Phone | NULL | 1 | | windows | Phone | NULL | 1 | | windows | PC | NULL | 3 | | linux | PC | NULL | 1 | | ios | Phone | NULL | 1 | | NULL | Phone | Beijing | 1 | | NULL | Phone | Shijiazhuang | 2 | | NULL | PC | Beijing | 3 | | NULL | PC | Shijiazhuang | 1 | | NULL | NULL | NULL | 7 | +---------+--------+--------------+----------+
GROUPING和GROUPING_ID函數(shù)
功能介紹
GROUPING函數(shù)
在GROUP BY子句使用GROUPING SETS、ROLLUP、或CUBE擴(kuò)展時,GROUPING SETS結(jié)果中會使用NULL來充當(dāng)占位符,導(dǎo)致無法區(qū)分占位符NULL與數(shù)據(jù)中真正的NULL。此時,您可以使用PolarDB-X 1.0提供的GROUPING函數(shù)來作區(qū)分。
GROUPING函數(shù)接受一個列名作為參數(shù),如果結(jié)果對應(yīng)行使用了參數(shù)列做聚合,則結(jié)果返回0,此時意味著NULL來自輸入數(shù)據(jù)。如果結(jié)果對應(yīng)行未使用參數(shù)列做聚合,則返回1,此時意味著NULL來自GROUPING SETS結(jié)果中的占位符。
GROUPING_ID函數(shù)
GROUPING_ID函數(shù)簡化了GROUPING函數(shù),用于確定ROLLBACK、CUBE或GROUPING SETS擴(kuò)展的結(jié)果集中行的小計級別。GROUPING函數(shù)僅采用一個列表達(dá)式并返回一個值來指示行是否為給定列的所有值的小計。因此,當(dāng)解釋具有多個分組列的查詢的小計級別時,可能需要多個 GROUPING函數(shù)。GROUPING_ID函數(shù)接受ROLLBACK、CUBE或GROUPINGSETS擴(kuò)展中已使用的一個或多個列表達(dá)式,并返回單個整數(shù),該整數(shù)可用于確定其中哪一列已聚合小計。
語法
GROUPING函數(shù)
SELECT [ expr ...,] GROUPING( col_expr ) [, expr ] ... FROM ... GROUP BY { ROLLUP | CUBE | GROUPING SETS }( [...,] col_expr [, ...] ) [, ...]
說明GROUPING函數(shù)采用單個參數(shù),該參數(shù)必須是GROUP BY子句中ROLLUP、CUBE或GROUPING SETS擴(kuò)展的表達(dá)式列表中指定的維度列的表達(dá)式。
GROUPING_ID函數(shù)
SELECT [ expr ...,] GROUPING_ID( col_expr_1 [, col_expr_2 ] ... ) [, expr ] ... FROM ... GROUP BY { ROLLUP | CUBE | GROUPING SETS }( [...,] col_expr_1 [, col_expr_2 ] [, ...] ) [, ...]
示例
通過GROUPING_ID函數(shù)將多個列名作為參數(shù),并將參數(shù)列的GROUPING結(jié)果按照Bitmap的方式組成整數(shù),語法如下:
select a,b,c,count(*), grouping(a) ga, grouping(b) gb, grouping(c) gc, grouping_id(a,b,c) groupingid from (select 1 as a ,2 as b,3 as c) group by cube(a,b,c);
返回結(jié)果如下:
+------+------+------+----------+------+------+------+------------+ | a | b | c | count(*) | ga | gb | gc | groupingid | +------+------+------+----------+------+------+------+------------+ | 1 | 2 | 3 | 1 | 0 | 0 | 0 | 0 | | 1 | 2 | NULL | 1 | 0 | 0 | 1 | 1 | | 1 | NULL | 3 | 1 | 0 | 1 | 0 | 2 | | 1 | NULL | NULL | 1 | 0 | 1 | 1 | 3 | | NULL | 2 | 3 | 1 | 1 | 0 | 0 | 4 | | NULL | 2 | NULL | 1 | 1 | 0 | 1 | 5 | | NULL | NULL | 3 | 1 | 1 | 1 | 0 | 6 | | NULL | NULL | NULL | 1 | 1 | 1 | 1 | 7 | +------+------+------+----------+------+------+------+------------+