跳至主要内容

SummingMergeTree

该引擎继承自 MergeTree。不同之处在于,当合并 SummingMergeTree 表的数据块时,ClickHouse 会用一行包含数值数据类型列的汇总值的行替换所有具有相同主键(或更准确地说,具有相同 排序键)的行。如果排序键的组成方式使得单个键值对应大量行,这将显着减少存储量并加快数据选择速度。

我们建议将该引擎与 MergeTree 一起使用。将完整数据存储在 MergeTree 表中,并使用 SummingMergeTree 存储聚合数据,例如,在准备报表时。这种方法可以防止由于主键组成不当而导致宝贵数据丢失。

创建表

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = SummingMergeTree([columns])
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]

有关请求参数的说明,请参阅 请求说明

SummingMergeTree 的参数

columns - 包含将汇总其值的列名称的元组。可选参数。这些列必须为数值类型,并且不能位于主键中。

如果未指定 columns,则 ClickHouse 会汇总所有不在主键中的数值数据类型列中的值。

查询子句

在创建 SummingMergeTree 表时,需要与创建 MergeTree 表时相同的 子句

创建表的已弃用方法
注意

在新项目中不要使用此方法,并且如果可能,请将旧项目切换到上述方法。

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE [=] SummingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [columns])

columns 之外的所有参数与 MergeTree 中的含义相同。

  • columns — 包含将汇总其值的列名称的元组。可选参数。有关说明,请参阅上面的文本。

使用示例

考虑以下表格

CREATE TABLE summtt
(
key UInt32,
value UInt32
)
ENGINE = SummingMergeTree()
ORDER BY key

向其中插入数据

INSERT INTO summtt Values(1,1),(1,2),(2,1)

ClickHouse 可能会对所有行进行不完全求和(见下文),因此我们在查询中使用聚合函数 sumGROUP BY 子句。

SELECT key, sum(value) FROM summtt GROUP BY key
┌─key─┬─sum(value)─┐
│ 2 │ 1 │
│ 1 │ 3 │
└─────┴────────────┘

数据处理

当数据插入到表中时,会按原样保存。ClickHouse 定期合并插入的数据块,此时具有相同主键的行会被求和并替换为每个生成的数据块的一个行。

ClickHouse 可以合并数据块,以便不同的生成数据块可以包含具有相同主键的行,即求和将是不完整的。因此(SELECT)应在查询中使用聚合函数 sum()GROUP BY 子句,如上例所示。

求和的通用规则

数值数据类型列中的值将被求和。列集由参数 columns 定义。

如果所有求和列中的值均为 0,则删除该行。

如果列不在主键中且未被求和,则从现有值中选择一个任意值。

主键中的列的值不会被求和。

聚合函数列中的求和

对于 AggregateFunction 类型 的列,ClickHouse 的行为类似于 AggregatingMergeTree 引擎,根据函数进行聚合。

嵌套结构

表可以具有以特殊方式处理的嵌套数据结构。

如果嵌套表的名称以 Map 结尾,并且它包含至少两列满足以下条件

  • 第一列是数值型(*Int*、Date、DateTime)或字符串型(String、FixedString),我们将其称为 key
  • 其他列是算术型(*Int*、Float32/64),我们将其称为 (values...)

那么此嵌套表将被解释为 key => (values...) 的映射,并且在合并其行时,两个数据集的元素将按 key 合并,并对相应的 (values...) 求和。

示例

DROP TABLE IF EXISTS nested_sum;
CREATE TABLE nested_sum
(
date Date,
site UInt32,
hitsMap Nested(
browser String,
imps UInt32,
clicks UInt32
)
) ENGINE = SummingMergeTree
PRIMARY KEY (date, site);

INSERT INTO nested_sum VALUES ('2020-01-01', 12, ['Firefox', 'Opera'], [10, 5], [2, 1]);
INSERT INTO nested_sum VALUES ('2020-01-01', 12, ['Chrome', 'Firefox'], [20, 1], [1, 1]);
INSERT INTO nested_sum VALUES ('2020-01-01', 12, ['IE'], [22], [0]);
INSERT INTO nested_sum VALUES ('2020-01-01', 10, ['Chrome'], [4], [3]);

OPTIMIZE TABLE nested_sum FINAL; -- emulate merge

SELECT * FROM nested_sum;
┌───────date─┬─site─┬─hitsMap.browser───────────────────┬─hitsMap.imps─┬─hitsMap.clicks─┐
│ 2020-01-01 │ 10 │ ['Chrome'] │ [4] │ [3] │
│ 2020-01-01 │ 12 │ ['Chrome','Firefox','IE','Opera'] │ [20,11,22,5] │ [1,3,0,1] │
└────────────┴──────┴───────────────────────────────────┴──────────────┴────────────────┘

SELECT
site,
browser,
impressions,
clicks
FROM
(
SELECT
site,
sumMap(hitsMap.browser, hitsMap.imps, hitsMap.clicks) AS imps_map
FROM nested_sum
GROUP BY site
)
ARRAY JOIN
imps_map.1 AS browser,
imps_map.2 AS impressions,
imps_map.3 AS clicks;

┌─site─┬─browser─┬─impressions─┬─clicks─┐
│ 12 │ Chrome │ 20 │ 1 │
│ 12 │ Firefox │ 11 │ 3 │
│ 12 │ IE │ 22 │ 0 │
│ 12 │ Opera │ 5 │ 1 │
│ 10 │ Chrome │ 4 │ 3 │
└──────┴─────────┴─────────────┴────────┘

在请求数据时,请使用 sumMap(key, value) 函数对 Map 进行聚合。

对于嵌套数据结构,您无需在求和列的元组中指定其列。