ClickHouse/docs/zh/sql-reference/statements/select/group-by.md
2020-06-22 11:45:22 +03:00

8.2 KiB
Raw Blame History

machine_translated machine_translated_rev toc_title
true 5decc73b5d GROUP BY

GROUP BY子句

GROUP BY 子句切换 SELECT 查询转换为聚合模式,其工作原理如下:

  • GROUP BY 子句包含表达式列表或单个表达式其被认为是长度为1的列表。 这份名单充当 “grouping key”而每个单独的表达式将被称为 “key expressions”.
  • 在所有的表达式 SELECT, HAVING,和 ORDER BY 条款 必须 基于键表达式进行计算 聚合函数 在非键表达式(包括纯列)上。 换句话说,从表中选择的每个列必须用于键表达式或聚合函数内,但不能同时使用。
  • 聚合结果 SELECT 查询将包含尽可能多的行,因为有唯一值 “grouping key” 在源表中。 通常这会显着减少行数,通常是数量级,但不一定:如果所有行数保持不变 “grouping key” 值是不同的。

!!! note "注" 还有一种额外的方法可以在表上运行聚合。 如果查询仅在聚合函数中包含表列,则 GROUP BY clause 可以省略,并且通过一个空的键集合来假定聚合。 这样的查询总是只返回一行。

空处理

对于分组ClickHouse解释 NULL 作为一个值,并且 NULL==NULL. 它不同于 NULL 在大多数其他上下文中进行处理。

这里有一个例子来说明这意味着什么。

假设你有这张桌子:

┌─x─┬────y─┐
│ 1 │    2 │
│ 2 │ ᴺᵁᴸᴸ │
│ 3 │    2 │
│ 3 │    3 │
│ 3 │ ᴺᵁᴸᴸ │
└───┴──────┘

查询 SELECT sum(x), y FROM t_null_big GROUP BY y 结果:

┌─sum(x)─┬────y─┐
│      4 │    2 │
│      3 │    3 │
│      5 │ ᴺᵁᴸᴸ │
└────────┴──────┘

你可以看到 GROUP BYy = NULL 总结 x,仿佛 NULL 是这个值。

如果你通过几个键 GROUP BY,结果会给你选择的所有组合,就好像 NULL 是一个特定的值。

使用总计修饰符

如果 WITH TOTALS 指定修饰符,将计算另一行。 此行将具有包含默认值(零或空行)的关键列,以及包含跨所有行计算值的聚合函数列( “total” 值)。

这个额外的行仅产生于 JSON*, TabSeparated*,和 Pretty* 格式,与其他行分开:

  • JSON* 格式,这一行是作为一个单独的输出 totals 场。
  • TabSeparated* 格式,该行位于主结果之后,前面有一个空行(在其他数据之后)。
  • Pretty* 格式时,该行在主结果之后作为单独的表输出。
  • 在其他格式中,它不可用。

WITH TOTALS 可以以不同的方式运行时 HAVING 是存在的。 该行为取决于 totals_mode 设置。

配置合计处理

默认情况下, totals_mode = 'before_having'. 在这种情况下, totals 是跨所有行计算,包括那些不通过具有和 max_rows_to_group_by.

其他替代方案仅包括通过具有在 totals并与设置不同的行为 max_rows_to_group_bygroup_by_overflow_mode = 'any'.

after_having_exclusive Don't include rows that didn't pass through max_rows_to_group_by. 换句话说, totals 将有少于或相同数量的行,因为它会 max_rows_to_group_by 被省略。

after_having_inclusive Include all the rows that didn't pass through max_rows_to_group_bytotals. 换句话说, totals 将有多个或相同数量的行,因为它会 max_rows_to_group_by 被省略。

after_having_auto Count the number of rows that passed through HAVING. If it is more than a certain amount (by default, 50%), include all the rows that didn't pass through max_rows_to_group_bytotals. 否则,不包括它们。

totals_auto_threshold By default, 0.5. The coefficient for after_having_auto.

如果 max_rows_to_group_bygroup_by_overflow_mode = 'any' 不使用,所有的变化 after_having 是相同的,你可以使用它们中的任何一个(例如, after_having_auto).

您可以使用 WITH TOTALS 在子查询中,包括在子查询 JOIN 子句(在这种情况下,将各自的总值合并)。

示例:

SELECT
    count(),
    median(FetchTiming > 60 ? 60 : FetchTiming),
    count() - sum(Refresh)
FROM hits

但是与标准SQL相比如果表没有任何行根本没有任何行或者在使用WHERE to filter之后没有任何行则返回一个空结果而不是来自包含聚合函数初始值的行之

相对于MySQL并且符合标准SQL您无法获取不在键或聚合函数常量表达式除外中的某些列的某些值。 要解决此问题,您可以使用 any 聚合函数(获取第一个遇到的值)或 min/max.

示例:

SELECT
    domainWithoutWWW(URL) AS domain,
    count(),
    any(Title) AS title -- getting the first occurred page header for each domain.
FROM hits
GROUP BY domain

对于遇到的每个不同的键值, GROUP BY 计算一组聚合函数值。

GROUP BY 不支持数组列。

不能将常量指定为聚合函数的参数。 示例: sum(1). 相反,你可以摆脱常数。 示例: count().

实施细节

聚合是面向列的DBMS最重要的功能之一因此它的实现是ClickHouse中最优化的部分之一。 默认情况下,聚合使用哈希表在内存中完成。 它有40+的专业化是自动选择取决于 “grouping key” 数据类型。

在外部存储器中分组

您可以启用将临时数据转储到磁盘以限制内存使用期间 GROUP BY. 该 max_bytes_before_external_group_by 设置确定倾销的阈值RAM消耗 GROUP BY 临时数据到文件系统。 如果设置为0默认值它将被禁用。

使用时 max_bytes_before_external_group_by,我们建议您设置 max_memory_usage 大约两倍高。 这是必要的因为聚合有两个阶段读取数据和形成中间数据1和合并中间数据2。 将数据转储到文件系统只能在阶段1中发生。 如果未转储临时数据则阶段2可能需要与阶段1相同的内存量。

例如,如果 max_memory_usage 设置为10000000000你想使用外部聚合这是有意义的设置 max_bytes_before_external_group_by 到10000000000max_memory_usage 到200亿。 当触发外部聚合如果至少有一个临时数据转储RAM的最大消耗仅略高于 max_bytes_before_external_group_by.

通过分布式查询处理,在远程服务器上执行外部聚合。 为了使请求者服务器只使用少量的RAM设置 distributed_aggregation_memory_efficient 到1。

当合并数据刷新到磁盘时,以及当合并来自远程服务器的结果时, distributed_aggregation_memory_efficient 设置被启用,消耗高达 1/256 * the_number_of_threads 从RAM的总量。

当启用外部聚合时,如果有小于 max_bytes_before_external_group_by of data (i.e. data was not flushed), the query runs just as fast as without external aggregation. If any temporary data was flushed, the run time will be several times longer (approximately three times).

如果你有一个 ORDER BY 用一个 LIMITGROUP BY然后使用的RAM的量取决于数据的量 LIMIT,不是在整个表。 但如果 ORDER BY 没有 LIMIT,不要忘记启用外部排序 (max_bytes_before_external_sort).