跳至主要内容
跳至主要内容
编辑此页

级联物化视图

本示例演示如何创建物化视图,以及如何将第二个物化视图级联到第一个物化视图。在本页中,您将看到如何操作,许多可能性以及限制。可以通过使用第二个物化视图作为源来创建物化视图来解答不同的用例。


示例

我们将使用一个模拟数据集,其中包含一组域名每小时的访问量。

我们的目标

  1. 我们需要按月聚合每个域名的的数据,
  2. 我们还需要按年聚合每个域名的的数据。

您可以选择以下选项之一

  • 编写在 SELECT 请求期间读取和聚合数据的查询
  • 在导入时将数据准备成新的格式
  • 在导入时将数据准备成特定的聚合。

使用物化视图准备数据将允许您限制 ClickHouse 需要处理的数据量和计算量,从而使您的 SELECT 请求更快。

物化视图的源表

创建源表,因为我们的目标涉及报告聚合的数据而不是单个行,我们可以解析它,将信息传递给物化视图,并丢弃实际的传入数据。这满足了我们的目标并节省了存储空间,因此我们将使用 Null 表引擎。

CREATE DATABASE IF NOT EXISTS analytics;
CREATE TABLE analytics.hourly_data
(
    `domain_name` String,
    `event_time` DateTime,
    `count_views` UInt64
)
ENGINE = Null
注意

您可以在 Null 表上创建物化视图。因此,写入表的数据最终会影响视图,但原始原始数据仍然会被丢弃。

每月聚合表和物化视图

对于第一个物化视图,我们需要创建 Target 表,在本例中,它将是 analytics.monthly_aggregated_data,我们将存储按月和域名聚合的访问量总和。

CREATE TABLE analytics.monthly_aggregated_data
(
    `domain_name` String,
    `month` Date,
    `sumCountViews` AggregateFunction(sum, UInt64)
)
ENGINE = AggregatingMergeTree
ORDER BY (domain_name, month)

将数据转发到目标表的物化视图如下所示

CREATE MATERIALIZED VIEW analytics.monthly_aggregated_data_mv
TO analytics.monthly_aggregated_data
AS
SELECT
    toDate(toStartOfMonth(event_time)) AS month,
    domain_name,
    sumState(count_views) AS sumCountViews
FROM analytics.hourly_data
GROUP BY
    domain_name,
    month

年度聚合表和物化视图

现在我们将创建第二个物化视图,它将链接到我们之前的目标表 monthly_aggregated_data

首先,我们将创建一个新的目标表,该表将存储按年聚合的每个域名的访问量总和。

CREATE TABLE analytics.year_aggregated_data
(
    `domain_name` String,
    `year` UInt16,
    `sumCountViews` UInt64
)
ENGINE = SummingMergeTree()
ORDER BY (domain_name, year)

此步骤定义了级联。FROM 语句将使用 monthly_aggregated_data 表,这意味着数据流将是

  1. 数据进入 hourly_data 表。
  2. ClickHouse 将接收到的数据转发到第一个物化视图 monthly_aggregated_data 表,
  3. 最后,在步骤 2 中接收到的数据将被转发到 year_aggregated_data
CREATE MATERIALIZED VIEW analytics.year_aggregated_data_mv
TO analytics.year_aggregated_data
AS
SELECT
    toYear(toStartOfYear(month)) AS year,
    domain_name,
    sumMerge(sumCountViews) AS sumCountViews
FROM analytics.monthly_aggregated_data
GROUP BY
    domain_name,
    year
注意

在使用物化视图时的一个常见误解是数据是从表中读取的,这并不是 物化视图 的工作方式;转发的是插入的块,而不是表中最终的结果。

让我们想象在本例中,在 monthly_aggregated_data 中使用的引擎是 CollapsingMergeTree,转发到我们第二个物化视图 year_aggregated_data_mv 的数据将不是折叠表的最终结果,它将转发包含在 SELECT ... GROUP BY 中定义的字段的数据块。

如果您正在使用 CollapsingMergeTree、ReplacingMergeTree 或甚至 SummingMergeTree 并且计划创建级联物化视图,则需要了解此处描述的限制。

示例数据

现在是时候通过插入一些数据来测试我们的级联物化视图了

INSERT INTO analytics.hourly_data (domain_name, event_time, count_views)
VALUES ('clickhouse.com', '2019-01-01 10:00:00', 1),
       ('clickhouse.com', '2019-02-02 00:00:00', 2),
       ('clickhouse.com', '2019-02-01 00:00:00', 3),
       ('clickhouse.com', '2020-01-01 00:00:00', 6);

如果您选择 analytics.hourly_data 的内容,您会看到以下内容,因为表引擎是 Null,但数据已被处理。

SELECT * FROM analytics.hourly_data
Ok.

0 rows in set. Elapsed: 0.002 sec.

我们使用了一个小数据集,以确保我们可以跟踪并比较结果与我们期望的结果,一旦您的流程使用小数据集正确,您可以移动到大量数据。

结果

如果您尝试通过选择 sumCountViews 字段来查询目标表,您会看到二进制表示形式(在某些终端中),因为该值未存储为数字,而是作为 AggregateFunction 类型存储的。要获得聚合的最终结果,您应该使用 -Merge 后缀。

您可以使用此查询查看存储在 AggregateFunction 中的特殊字符

SELECT sumCountViews FROM analytics.monthly_aggregated_data
┌─sumCountViews─┐
│               │
│               │
│               │
└───────────────┘

3 rows in set. Elapsed: 0.003 sec.

相反,让我们尝试使用 Merge 后缀来获取 sumCountViews

SELECT
   sumMerge(sumCountViews) AS sumCountViews
FROM analytics.monthly_aggregated_data;
┌─sumCountViews─┐
│            12 │
└───────────────┘

1 row in set. Elapsed: 0.003 sec.

AggregatingMergeTree 中,我们将 AggregateFunction 定义为 sum,因此我们可以使用 sumMerge。当我们在 AggregateFunction 上使用函数 avg 时,我们将使用 avgMerge,依此类推。

SELECT
    month,
    domain_name,
    sumMerge(sumCountViews) AS sumCountViews
FROM analytics.monthly_aggregated_data
GROUP BY
    domain_name,
    month

现在我们可以查看物化视图是否回答了我们定义的目标。

现在我们已经在目标表 monthly_aggregated_data 中存储了数据,我们可以按月获取每个域名的聚合数据

SELECT
   month,
   domain_name,
   sumMerge(sumCountViews) AS sumCountViews
FROM analytics.monthly_aggregated_data
GROUP BY
   domain_name,
   month
┌──────month─┬─domain_name────┬─sumCountViews─┐
│ 2020-01-01 │ clickhouse.com │             6 │
│ 2019-01-01 │ clickhouse.com │             1 │
│ 2019-02-01 │ clickhouse.com │             5 │
└────────────┴────────────────┴───────────────┘

3 rows in set. Elapsed: 0.004 sec.

按年聚合的每个域名的的数据

SELECT
   year,
   domain_name,
   sum(sumCountViews)
FROM analytics.year_aggregated_data
GROUP BY
   domain_name,
   year
┌─year─┬─domain_name────┬─sum(sumCountViews)─┐
│ 2019 │ clickhouse.com │                  6 │
│ 2020 │ clickhouse.com │                  6 │
└──────┴────────────────┴────────────────────┘

2 rows in set. Elapsed: 0.004 sec.

将多个源表组合到单个目标表

物化视图还可以用于将多个源表组合到同一个目标表中。这对于创建类似于 UNION ALL 逻辑的物化视图很有用。

首先,创建代表不同指标集的两个源表

CREATE TABLE analytics.impressions
(
    `event_time` DateTime,
    `domain_name` String
) ENGINE = MergeTree ORDER BY (domain_name, event_time)
;

CREATE TABLE analytics.clicks
(
    `event_time` DateTime,
    `domain_name` String
) ENGINE = MergeTree ORDER BY (domain_name, event_time)
;

然后创建带有组合指标集的 Target

CREATE TABLE analytics.daily_overview
(
    `on_date` Date,
    `domain_name` String,
    `impressions` SimpleAggregateFunction(sum, UInt64),
    `clicks` SimpleAggregateFunction(sum, UInt64)
) ENGINE = AggregatingMergeTree ORDER BY (on_date, domain_name)

创建两个指向相同 Target 表的物化视图。您无需显式包含缺失的列

CREATE MATERIALIZED VIEW analytics.daily_impressions_mv
TO analytics.daily_overview
AS
SELECT
    toDate(event_time) AS on_date,
    domain_name,
    count() AS impressions,
    0 clicks         ---<<<--- if you omit this, it will be the same 0
FROM
    analytics.impressions
GROUP BY
    toDate(event_time) AS on_date,
    domain_name
;

CREATE MATERIALIZED VIEW analytics.daily_clicks_mv
TO analytics.daily_overview
AS
SELECT
    toDate(event_time) AS on_date,
    domain_name,
    count() AS clicks,
    0 impressions    ---<<<--- if you omit this, it will be the same 0
FROM
    analytics.clicks
GROUP BY
    toDate(event_time) AS on_date,
    domain_name
;

现在,当您插入值时,这些值将被聚合到 Target 表的各自的列中

INSERT INTO analytics.impressions (domain_name, event_time)
VALUES ('clickhouse.com', '2019-01-01 00:00:00'),
       ('clickhouse.com', '2019-01-01 12:00:00'),
       ('clickhouse.com', '2019-02-01 00:00:00'),
       ('clickhouse.com', '2019-03-01 00:00:00')
;

INSERT INTO analytics.clicks (domain_name, event_time)
VALUES ('clickhouse.com', '2019-01-01 00:00:00'),
       ('clickhouse.com', '2019-01-01 12:00:00'),
       ('clickhouse.com', '2019-03-01 00:00:00')
;

Target 表中组合的印象和点击量

SELECT
    on_date,
    domain_name,
    sum(impressions) AS impressions,
    sum(clicks) AS clicks
FROM
    analytics.daily_overview
GROUP BY
    on_date,
    domain_name
;

此查询应输出如下内容

┌────on_date─┬─domain_name────┬─impressions─┬─clicks─┐
│ 2019-01-01 │ clickhouse.com │           2 │      2 │
│ 2019-03-01 │ clickhouse.com │           1 │      1 │
│ 2019-02-01 │ clickhouse.com │           1 │      0 │
└────────────┴────────────────┴─────────────┴────────┘

3 rows in set. Elapsed: 0.018 sec.
    © . This site is unofficial and not affiliated with ClickHouse, Inc.