GROUP BY 子句通常与聚合函数一起使用,按照一列或多列的值对查询结果中的数据行分组,为每个不同的组合生成一个汇总行。除显式指定分组键外,还可以使用 GROUP BY ALL 根据 SELECT 列表自动确定分组键,或搭配 ROLLUP、CUBE 和 GROUPING SETS 子句扩展分组功能。
基本语法
可根据分析场景选择以下分组写法,其中 ROLLUP、CUBE、GROUPING SETS 在 GROUP BY 基础上扩展分组功能:
分析场景 | 适用写法 |
按显式指定的列或表达式对分析结果分组(最常用) |
|
分组键较多或需要调整分析维度,由 |
|
为每个分组返回一个小计,为所有分组返回一个总计 |
|
按照所有可能的列组合分组 |
|
按照指定的多个分组集合分别分组 |
|
在 SQL 语句中,普通分组查询的 SELECT 列表只能包含以下内容:
GROUP BY子句中指定的列或表达式对任意列的聚合计算结果,如
COUNT()、SUM()等使用
GROUP BY ALL时,分组键由SELECT列表自动推导,无需在GROUP BY子句中重复列出。还可以在分组结果上使用窗口函数,参见示例 9。
直接选择非 GROUP BY 列是不允许的,因为这些列的值在分组后可能不唯一,无法明确其含义。例如,以下语句是非法的:
* | SELECT status, request_time, COUNT(*) AS PV GROUP BY status原因是 request_time 并未包含在 GROUP BY 子句中,也未聚合。正确的写法可以是:
* | SELECT status, arbitrary(request_time), COUNT(*) AS PV GROUP BY statusarbitrary(request_time) 是对 request_time 聚合的一种方式,表示从每个分组中任意选取一个 request_time 值,既符合 SQL 语法规则,又能满足查询需求。
GROUP BY
对分析结果分组。
SELECT
key1,
...,
aggregate_function
GROUP BY key,...参数说明:
key1:分组键,即用于分组的列,支持按日志字段名称或聚合函数计算结果列分组。GROUP BY子句支持单列或多列。aggregate_function:应用在每个分组上的聚合函数,如count、min、max、avg和sum等。
完整示例参见示例 1~示例 3。
GROUP BY ALL
GROUP BY ALL 自动将 SELECT 列表中不包含聚合函数、也不包含窗口函数的表达式作为分组键,无需在 GROUP BY 子句中重复列出这些表达式。适合分组键较多,或需要调整分析维度的场景。
SELECT
key1,
key2,
aggregate_function
GROUP BY ALL参数说明:
key1、key2:分组键,可以是字段或表达式,例如status、request_method、substr(request_method, 1, 1)或CASE表达式。表达式按其完整计算结果分组;GROUP BY ALL后不跟任何字段或别名,SELECT列表中已设置的别名无需重复引用。aggregate_function:应用在每个分组上的聚合函数,例如count(*)、sum(request_time)和avg(request_time)。
例如,以下两条语句等价:
* | SELECT request_method, status, count(*) AS PV GROUP BY ALL* | SELECT request_method, status, count(*) AS PV GROUP BY request_method, statusSELECT 列表的通用合法性规则参见本章开头的重要提示,GROUP BY ALL 的使用规则和限制如下:
自动分组以当前
SELECT列表为准,不会按日志中的全部字段分组。增加或删除非聚合、非窗口表达式会相应改变分组维度。不支持在同一层查询中将
GROUP BY ALL与SELECT *、SELECT 表别名.*或SELECT *, count(*)组合使用,请显式列出字段。该限制针对SELECT *这类通配选择,显式列出字段后使用count(*)等聚合函数不受影响。包含聚合函数或窗口函数的整个表达式不会被自动加入分组键,例如
sum(request_time) / count(*)整体不参与分组。如果此类表达式之外还直接选择了未聚合的字段(例如未经arbitrary()等聚合函数处理的request_time),仍须满足普通GROUP BY的分组规则。当
SELECT列表只有聚合表达式时,对全部符合查询条件的数据全局聚合。例如* | SELECT count(*) AS PV, sum(request_time) AS total_time GROUP BY ALL与省略GROUP BY ALL的写法等价。支持与
WHERE、HAVING、ORDER BY以及窗口函数配合使用。窗口函数基于分组聚合后的结果计算,其引用的字段仍需满足分组规则。只有单独书写的
GROUP BY ALL才会自动推导分组键,GROUP BY ALL status或GROUP BY ALL ROLLUP (...)中的ALL不会触发自动推导。需要按名为
all的字段或别名分组时,请使用双引号,例如GROUP BY "all"。默认情况下,即使存在名为all的字段,单独的GROUP BY ALL仍会自动推导分组键。
完整示例参见示例 7~示例 9。
GROUP BY ROLLUP
GROUP BY ROLLUP 子句按汇总层级分组,为每个分组返回一个小计,为所有分组返回一个总计。例如 GROUP BY ROLLUP (a, b) 的结果集为 (a, b)、(a, null) 和 (null, null)。完整示例参见示例 6。
SELECT
key1,
...,
aggregate_function
GROUP BY ROLLUP (key1,...)参数说明如下所示:
key1:是您希望根据其值来分组的列,支持按照日志字段名称或聚合函数计算结果列进行分组。GROUP BY子句支持单列或多列。aggregate_function:是应用在每个分组上的聚合函数,如count、min、max、avg和sum等。
GROUP BY CUBE
GROUP BY CUBE 子句按照所有可能的列组合分组。例如 GROUP BY CUBE (a, b) 的结果集为 (a, b)、(null, b)、(a, null) 和 (null, null)。完整示例参见示例 5。
SELECT
key1,
...,
aggregate_function
GROUP BY CUBE (key1,...)参数说明如下所示:
key1:是您希望根据其值来分组的列,支持按照日志字段名称或聚合函数计算结果列进行分组。GROUP BY子句支持单列或多列。aggregate_function:是应用在每个分组上的聚合函数,如count、min、max、avg和sum等。
GROUP BY GROUPING SETS
GROUP BY GROUPING SETS 子句按照指定的多个分组集合分别分组,未参与某个分组集合的列在该集合的结果中为 null。例如 GROUP BY GROUPING SETS (a, b) 的结果集为 (a, null) 和 (null, b)。完整示例参见示例 4。
SELECT
key1,
...,
aggregate_function
GROUP BY GROUPING SETS (key1,...)参数说明如下所示:
key1:是您希望根据其值来分组的列,支持按照日志字段名称或聚合函数计算结果列进行分组。GROUP BY子句支持单列或多列。aggregate_function:是应用在每个分组上的聚合函数,如count、min、max、avg和sum等。
示例
示例 1:按状态码统计请求次数
统计不同状态码对应的请求次数。
查询和分析语句
* | SELECT status, count(*) AS PV GROUP BY status查询和分析结果以表格形式展示,包含 status 和 PV 两列,分别显示各 HTTP 状态码及其对应的页面访问量统计值。
示例 2:按小时统计网站访问 PV
按照每小时的时间粒度计算网站访问 PV。
查询和分析语句
__time__字段为日志服务中的保留字段,表示时间列;time为date_trunc('hour', __time__)的别名,函数用法参见 date_trunc 函数。
* |
SELECT
count(*) AS PV,
date_trunc('hour', __time__) AS time
GROUP BY
time
ORDER BY
time
LIMIT
1000查询和分析结果:返回包含 PV 和 time 两列的结果,示例数据:PV 分别为 1202、10159、28001,对应时间为 2021-08-10 00:00:00.000、2021-08-10 01:00:00.000、2021-08-10 02:00:00.000。
示例 3:按 5 分钟时间粒度统计 PV
按照每 5 分钟的时间粒度计算 PV。
查询和分析语句
由于 date_trunc 函数只能按照固定时间间隔统计,需要按照自定义时间统计分析时,请按照数学取模方法分组。例如 %300 表示按照 5 分钟的时间粒度取模对齐。
* |
SELECT
count(*) AS PV,
__time__-__time__ % 300 AS time
GROUP BY
time
LIMIT
1000查询和分析结果返回三条记录:PV=143 对应 time=1628525100,PV=31 对应 time=1628526600,PV=44 对应 time=1628526900。
示例 4:按指定分组集合统计访问数据 (GROUPING SETS)
根据请求方法和请求状态分组,先计算各个请求方法对应的访问数据,再计算各个请求状态对应的访问数量。
查询和分析语句
* |
SELECT
request_method,
status,
count(*) AS PV
GROUP BY
GROUPING SETS (request_method, status)查询和分析结果:按
request_method分组时,status为 null,各方法 PV 值分别为:GET=285、POST=51、DELETE=28、PUT=60;按status分组时,request_method为 null,各状态码 PV 值分别为:200=382、204=2、501=2、202=4、305=4。
示例 5:按所有列组合统计访问数据 (CUBE)
根据请求方法和请求状态分组,分组集包括 (null, null)、(request_method, null)、(null, status) 和 (request_method, status),计算以上各个分组的访问数量。
查询和分析语句
* |
SELECT
request_method,
status,
count(*) AS PV
GROUP BY
CUBE (request_method, status)查询和分析结果:查询结果包含 request_method、status、PV 三列,共 43 条记录。CUBE 分组产生的聚合行中,
request_method或status为 null 表示该维度的小计。例如:null/null 的 PV 为 285(总计),GET/null 为 189,POST/null 为 47,PUT/null 为 39,DELETE/null 为 9,HEAD/null 为 1;null/200 为 259,null/201 为 2,null/202 为 2。
示例 6:按汇总层级统计小计与总计 (ROLLUP)
根据请求方法和请求状态分组,分组集包括 (request_method, status)、(request_method, null) 和 (null, null),计算以上各个分组的访问数量。
查询和分析语句
* |
SELECT
request_method,
status,
count(*) AS PV
GROUP BY
ROLLUP (request_method, status)查询和分析结果:查询结果包含 request_method、status、PV 三列,共 33 条记录。ROLLUP 函数在各分组层级生成汇总行,其中
null表示该列已被聚合:例如GET / null / 162表示 GET 方法所有状态码的合计 PV 为 162,null / null / 285表示所有方法与状态码的总计 PV 为 285。
示例 7:自动按多个字段分组 (GROUP BY ALL)
根据请求方法和状态码统计请求次数,只保留请求次数大于 100 的分组。
查询和分析语句
* |
SELECT
request_method,
status,
count(*) AS PV
GROUP BY ALL
HAVING count(*) > 100
ORDER BY PV DESC查询和分析结果:包含 request_method、status 和 PV 三列,仅返回请求次数大于 100 的组合,并按 PV 从高到低排列。分组方式等价于
GROUP BY request_method, status。
示例 8:自动按表达式分组 (GROUP BY ALL)
按照每 5 分钟的时间粒度和请求方法统计 PV,无需重复填写时间表达式或其别名。
查询和分析语句
* |
SELECT
__time__ - __time__ % 300 AS time,
request_method,
count(*) AS PV
GROUP BY ALL
ORDER BY time, request_method
LIMIT 1000查询和分析结果:包含 time、request_method 和 PV 三列。分组方式等价于
GROUP BY __time__ - __time__ % 300, request_method。
示例 9:对分组结果计算排名 (GROUP BY ALL)
统计各状态码的请求次数,再按照请求次数从高到低计算排名。
查询和分析语句
* |
SELECT
status,
count(*) AS PV,
rank() OVER (ORDER BY count(*) DESC) AS ranking
GROUP BY ALL
ORDER BY ranking, status查询和分析结果:包含 status、PV 和 ranking 三列。仅
status作为分组键,count(*)用于聚合,rank()对聚合结果排名,不参与分组;请求次数相同的状态码具有相同排名。