slug: /zh/sql-reference/statements/create/view sidebar_position: 37

sidebar_label: VIEW

CREATE VIEW {#create-view}

创建一个新视图。 有两种类型的视图:普通视图,物化视图,Live视图和Window视图。

Normal {#normal}

语法:

  1. CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] AS SELECT ...

普通视图不存储任何数据。 他们只是在每次访问时从另一个表执行读取。换句话说,普通视图只不过是一个保存的查询。 从视图中读取时,此保存的查询用作FROM子句中的子查询.

例如,假设您已经创建了一个视图:

  1. CREATE VIEW view AS SELECT ...

并写了一个查询:

  1. SELECT a, b, c FROM view

这个查询完全等同于使用子查询:

  1. SELECT a, b, c FROM (SELECT ...)

Materialized {#materialized}

  1. CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...

物化视图存储由相应的SELECT管理.

创建不带TO [db].[table]的物化视图时,必须指定ENGINE – 用于存储数据的表引擎。

使用TO [db].[table] 创建物化视图时,不得使用POPULATE

一个物化视图的实现是这样的:当向SELECT中指定的表插入数据时,插入数据的一部分被这个SELECT查询转换,结果插入到视图中。

!!! important “重要” ClickHouse 中的物化视图更像是插入触发器。 如果视图查询中有一些聚合,则它仅应用于一批新插入的数据。 对源表现有数据的任何更改(如更新、删除、删除分区等)都不会更改物化视图。

如果指定POPULATE,则在创建视图时将现有表数据插入到视图中,就像创建一个CREATE TABLE ... AS SELECT ...一样。 否则,查询仅包含创建视图后插入表中的数据。 我们不建议使用POPULATE,因为在创建视图期间插入表中的数据不会插入其中。

SELECT 查询可以包含DISTINCTGROUP BYORDER BYLIMIT……请注意,相应的转换是在每个插入数据块上独立执行的。 例如,如果设置了GROUP BY,则在插入期间聚合数据,但仅在插入数据的单个数据包内。 数据不会被进一步聚合。 例外情况是使用独立执行数据聚合的ENGINE,例如SummingMergeTree

在物化视图上执行ALTER查询有局限性,因此可能不方便。 如果物化视图使用构造TO [db.]name,你可以DETACH视图,为目标表运行ALTER,然后ATTACH先前分离的(DETACH)视图。

请注意,物化视图受optimize_on_insert设置的影响。 在插入视图之前合并数据。

视图看起来与普通表相同。 例如,它们列在1SHOW TABLES1查询的结果中。

删除视图,使用DROP VIEW. DROP TABLE也适用于视图。

Live View (实验性) {#live-view}

!!! important “重要” 这是一项实验性功能,可能会在未来版本中以向后不兼容的方式进行更改。 使用allow_experimental_live_view设置启用实时视图和WATCH查询的使用。 输入命令set allow_experimental_live_view = 1

  1. CREATE LIVE VIEW [IF NOT EXISTS] [db.]table_name [WITH [TIMEOUT [value_in_sec] [AND]] [REFRESH [value_in_sec]]] AS SELECT ...

实时视图存储相应SELECT查询的结果,并在查询结果更改时随时更新。 查询结果以及与新数据结合所需的部分结果存储在内存中,为重复查询提供更高的性能。当使用WATCH查询更改查询结果时,实时视图可以提供推送通知。

实时视图是通过插入到查询中指定的最里面的表来触发的。

实时视图的工作方式类似于分布式表中查询的工作方式。 但不是组合来自不同服务器的部分结果,而是将当前数据的部分结果与新数据的部分结果组合在一起。当实时视图查询包含子查询时,缓存的部分结果仅存储在最里面的子查询中。

!!! info “限制”

  • Table function不支持作为最里面的表.
  • 没有插入的表,例如dictionary, system table, normal view, materialized view不会触发实时视图。
  • 只有可以将旧数据的部分结果与新数据的部分结果结合起来的查询才有效。 实时视图不适用于需要完整数据集来计算最终结果或必须保留聚合状态的聚合的查询。
  • 不适用于在不同节点上执行插入的复制或分布式表。
  • 不能被多个表触发。

    WITH REFRESH强制定期更新实时视图,在某些情况下可以用作解决方法。

Monitoring Changes {#live-view-monitoring}

您可以使用WATCH命令监视LIVE VIEW查询结果的变化

  1. WATCH [db.]live_view

示例:

  1. CREATE TABLE mt (x Int8) Engine = MergeTree ORDER BY x;
  2. CREATE LIVE VIEW lv AS SELECT sum(x) FROM mt;

在对源表进行并行插入时观看实时视图。

  1. WATCH lv;
  1. ┌─sum(x)─┬─_version─┐
  2. 1 1
  3. └────────┴──────────┘
  4. ┌─sum(x)─┬─_version─┐
  5. 2 2
  6. └────────┴──────────┘
  7. ┌─sum(x)─┬─_version─┐
  8. 6 3
  9. └────────┴──────────┘
  10. ...
  1. INSERT INTO mt VALUES (1);
  2. INSERT INTO mt VALUES (2);
  3. INSERT INTO mt VALUES (3);

添加EVENTS子句只获取更改事件。

  1. WATCH [db.]live_view EVENTS;

示例:

  1. WATCH lv EVENTS;
  1. ┌─version─┐
  2. 1
  3. └─────────┘
  4. ┌─version─┐
  5. 2
  6. └─────────┘
  7. ┌─version─┐
  8. 3
  9. └─────────┘
  10. ...

你可以执行SELECT与任何常规视图或表格相同的方式查询实时视图。如果查询结果被缓存,它将立即返回结果而不在基础表上运行存储的查询。

  1. SELECT * FROM [db.]live_view WHERE ...

Force Refresh {#live-view-alter-refresh}

您可以使用ALTER LIVE VIEW [db.]table_name REFRESH语法.

WITH REFRESH条件 {#live-view-with-refresh}

当使用WITH REFRESH子句创建实时视图时,它将在自上次刷新或触发后经过指定的秒数后自动刷新。

  1. CREATE LIVE VIEW [db.]table_name WITH REFRESH [value_in_sec] AS SELECT ...

如果未指定刷新值,则由指定的值periodic_live_view_refresh决定.

示例:

  1. CREATE LIVE VIEW lv WITH REFRESH 5 AS SELECT now();
  2. WATCH lv
  1. ┌───────────────now()─┬─_version─┐
  2. 2021-02-21 08:47:05 1
  3. └─────────────────────┴──────────┘
  4. ┌───────────────now()─┬─_version─┐
  5. 2021-02-21 08:47:10 2
  6. └─────────────────────┴──────────┘
  7. ┌───────────────now()─┬─_version─┐
  8. 2021-02-21 08:47:15 3
  9. └─────────────────────┴──────────┘
  1. WATCH lv
  1. Code: 60. DB::Exception: Received from localhost:9000. DB::Exception: Table default.lv does not exist..

Usage {#live-view-usage}

实时视图表的最常见用途包括:

  • 为查询结果更改提供推送通知以避免轮询。
  • 缓存最频繁查询的结果以提供即时查询结果。
  • 监视表更改并触发后续选择查询。
  • 使用定期刷新从系统表中查看指标。

原始文章

Window View [Experimental] {#window-view}

!!! important “重要” 这是一项试验性功能,可能会在未来版本中以向后不兼容的方式进行更改。 通过allow_experimental_window_view启用window view以及WATCH语句。输入命令 set allow_experimental_window_view = 1

  1. CREATE WINDOW VIEW [IF NOT EXISTS] [db.]table_name [TO [db.]table_name] [INNER ENGINE engine] [ENGINE engine] [WATERMARK strategy] [ALLOWED_LATENESS interval_function] [POPULATE] AS SELECT ... GROUP BY time_window_function

Window view可以通过时间窗口聚合数据,并在满足窗口触发条件时自动触发对应窗口计算。其通过将计算状态保存降低处理延迟,支持将处理结果输出至目标表或通过WATCH语句输出至终端。

创建window view的方式和创建物化视图类似。Window view通过INNER ENGINE指定内部存储引擎以存储窗口计算中间状态,默认使用AggregatingMergeTree作为内部中间状态存储引擎。

创建不带TO [db].[table]的window view时,必须指定ENGINE – 用于存储数据的表引擎。

时间窗口函数 {#window-view-shi-jian-chuang-kou-han-shu}

时间窗口函数用于获取窗口的起始和结束时间。Window view需要和时间窗口函数配合使用。

时间属性 {#window-view-shi-jian-shu-xing}

Window view 支持处理时间事件时间两种时间类型。

处理时间为默认时间类型,该模式下window view使用本地机器时间计算窗口数据。“处理时间”时间类型计算简单,但具有不确定性。该模式下时间可以为时间窗口函数的第一个参数time_attr,或通过函数now()使用当前机器时间。下面的例子展示了使用“处理时间”创建window view的例子。

  1. CREATE WINDOW VIEW wv AS SELECT count(number), tumbleStart(w_id) as w_start from date GROUP BY tumble(now(), INTERVAL '5' SECOND) as w_id

事件时间 是事件真实发生的时间,该时间往往在事件发生时便嵌入数据记录。事件时间处理提供较高的确定性,可以处理乱序数据以及迟到数据。Window view通过水位线(WATERMARK)启用事件时间处理。

Window view提供如下三种水位线策略:

  • STRICTLY_ASCENDING: 提交观测到的最大时间作为水位线,小于最大观测时间的数据不算迟到。
  • ASCENDING: 提交观测到的最大时间减1作为水位线。小于或等于最大观测时间的数据不算迟到。
  • BOUNDED: WATERMARK=INTERVAL. 提交最大观测时间减去固定间隔(INTERVAL)做为水位线。

以下为使用WATERMARK创建window view的示例:

  1. CREATE WINDOW VIEW wv WATERMARK=STRICTLY_ASCENDING AS SELECT count(number) FROM date GROUP BY tumble(timestamp, INTERVAL '5' SECOND);
  2. CREATE WINDOW VIEW wv WATERMARK=ASCENDING AS SELECT count(number) FROM date GROUP BY tumble(timestamp, INTERVAL '5' SECOND);
  3. CREATE WINDOW VIEW wv WATERMARK=INTERVAL '3' SECOND AS SELECT count(number) FROM date GROUP BY tumble(timestamp, INTERVAL '5' SECOND);

通常,窗口会在水位线到达时触发,水位线到达之后的数据会被丢弃。Window view可以通过设置ALLOWED_LATENESS=INTERVAL来开启迟到消息处理。示例如下:

  1. CREATE WINDOW VIEW test.wv TO test.dst WATERMARK=ASCENDING ALLOWED_LATENESS=INTERVAL '2' SECOND AS SELECT count(a) AS count, tumbleEnd(wid) AS w_end FROM test.mt GROUP BY tumble(timestamp, INTERVAL '5' SECOND) AS wid;

需要注意的是,迟到消息需要更新之前的处理结果。与在窗口结束时触发不同,迟到消息到达时window view会立即触发计算。因此,会导致同一个窗口输出多次计算结果。用户需要注意这种情况,并消除重复结果。

查询语句修改 {#window-view-cha-xun-yu-ju-xiu-gai}

用户可以通过ALTER TABLE ... MODIFY QUERY语句修改window view的SELECT查询语句。无论是否使用TO [db.]name语句,新SELECT语句的数据结构均需和旧语句相同。需要注意的是,由于窗口计算中间状态无法复用,修改查询语句时会丢失当前窗口数据。

新窗口监控 {#window-view-xin-chuang-kou-jian-kong}

Window view可以通过WATCH语句将处理结果推送至终端,或通过TO语句将结果推送至数据表。

  1. WATCH [db.]name [LIMIT n]

WATCH语句和LIVE VIEW中的类似。支持设置LIMIT参数,输出消息数目达到LIMIT限制时结束查询。

设置 {#window-view-she-zhi}

  • window_view_clean_interval: window view清除过期数据间隔(单位为秒)。系统会定期清除过期数据,尚未触发的窗口数据不会被清除。
  • window_view_heartbeat_interval: 用于判断watch查询活跃的心跳时间间隔。
  • wait_for_window_view_fire_signal_timeout: Event time 处理模式下,窗口触发信号等待超时时间。

示例 {#window-view-shi-li}

假设我们需要每10秒统计一次data表中的点击日志,且data表的结构如下:

  1. CREATE TABLE data ( `id` UInt64, `timestamp` DateTime) ENGINE = Memory;

首先,使用10秒大小的tumble函数创建window view。

  1. CREATE WINDOW VIEW wv as select count(id), tumbleStart(w_id) as window_start from data group by tumble(timestamp, INTERVAL '10' SECOND) as w_id

随后,我们使用WATCH语句获取计算结果。

  1. WATCH wv

当日志插入表data时,

  1. INSERT INTO data VALUES(1,now())

WATCH语句会输出如下结果:

  1. ┌─count(id)─┬────────window_start─┐
  2. 1 2020-01-14 16:56:40
  3. └───────────┴─────────────────────┘

或者,我们可以通过TO关键字将处理结果输出至另一张表。

  1. CREATE WINDOW VIEW wv TO dst AS SELECT count(id), tumbleStart(w_id) as window_start FROM data GROUP BY tumble(timestamp, INTERVAL '10' SECOND) as w_id

ClickHouse测试中提供了更多的示例(以*window_view*命名)。

Window View 使用场景 {#window-view-shi-yong-chang-jing}

Window view 在以下场景有用:

  • 监控: 以时间维度聚合及处理数据,并将处理结果输出至目标表。用户可通过目标表获取并操作计算结果。
  • 分析: 以时间维度进行数据分析. 当数据源非常庞大时,window view可以减少重复全表查询的计算量。