> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> CREATE VIEW 相关文档

# CREATE VIEW

export const DeprecatedBadge = () => {
  return <div className="deprecatedBadge">
            <div className="deprecatedIcon">
            <svg width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M13 0H1C0.734784 0 0.48043 0.105357 0.292893 0.292893C0.105357 0.48043 0 0.734784 0 1V2.5C0 2.76522 0.105357 3.01957 0.292893 3.20711C0.48043 3.39464 0.734784 3.5 1 3.5V9C1 9.26522 1.10536 9.51957 1.29289 9.70711C1.48043 9.89464 1.73478 10 2 10H12C12.2652 10 12.5196 9.89464 12.7071 9.70711C12.8946 9.51957 13 9.26522 13 9V3.5C13.2652 3.5 13.5196 3.39464 13.7071 3.20711C13.8946 3.01957 14 2.76522 14 2.5V1C14 0.734784 13.8946 0.48043 13.7071 0.292893C13.5196 0.105357 13.2652 0 13 0ZM12 9H2V3.5H12V9ZM13 2.5H1V1H13V2.5ZM5 5.5C5 5.36739 5.05268 5.24021 5.14645 5.14645C5.24021 5.05268 5.36739 5 5.5 5H8.5C8.63261 5 8.75979 5.05268 8.85355 5.14645C8.94732 5.24021 9 5.36739 9 5.5C9 5.63261 8.94732 5.75979 8.85355 5.85355C8.75979 5.94732 8.63261 6 8.5 6H5.5C5.36739 6 5.24021 5.94732 5.14645 5.85355C5.05268 5.75979 5 5.63261 5 5.5Z" fill="currentColor" />
            </svg>
        </div>
            已弃用功能
        </div>;
};

创建一个新视图。视图可以是[普通](#normal-view)、[materialized](#materialized-view)和[可刷新的 materialized](#refreshable-materialized-view)。

## 普通视图

语法：

```sql theme={null}
CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [(alias1 [, alias2 ...])] [ON CLUSTER cluster_name]
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
AS SELECT ...
[COMMENT 'comment']
```

普通视图不存储任何数据。每次访问时，它们只是从另一张表中读取数据。换句话说，普通视图无非就是一个已保存的查询。从视图读取时，这个已保存的查询会作为子查询用于 [FROM](/zh/reference/statements/select/from) 子句中。

例如，假设你已经创建了一个视图：

```sql theme={null}
CREATE VIEW view AS SELECT ...
```

并编写了一个查询：

```sql theme={null}
SELECT a, b, c FROM view
```

此查询与使用该子查询完全等同：

```sql theme={null}
SELECT a, b, c FROM (SELECT ...)
```

## 参数化视图

参数化视图与普通视图类似，但可以在创建时定义不会立即解析的参数。
这类视图可与表函数配合使用：将视图名称作为函数名，将参数值作为其参数传入。

```sql theme={null}
CREATE VIEW view AS SELECT * FROM TABLE WHERE Column1={column1:datatype1} and Column2={column2:datatype2} ...
```

上述内容会为该表创建一个视图，通过替换参数，可按如下方式将其用作表函数。

```sql theme={null}
SELECT * FROM view(column1=value1, column2=value2 ...)
```

由于参数化视图依赖于参数值，未提供参数时便没有 schema。
这意味着 `system.columns` 表中没有参数化视图的相关信息。
此外，只有提供参数后，`DESCRIBE` 查询才能执行。

```sql theme={null}
DESCRIBE view(column1=value1, column2=value2 ...)
```

## Materialized View

```sql theme={null}
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] [TO[db.]name [(columns)]] [ENGINE = engine] [POPULATE]
[REFRESH ...]
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | NONE }]
AS SELECT ...
[COMMENT 'comment']
```

```sql theme={null}
CREATE OR REPLACE MATERIALIZED VIEW [db.]table_name [ON CLUSTER cluster_name] [TO[db.]name [(columns)]] [ENGINE = engine] [POPULATE]
[REFRESH ...]
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | NONE }]
AS SELECT ...
[COMMENT 'comment']
```

`OR REPLACE` 和 `IF NOT EXISTS` 互斥：不能同时使用，否则会报语法错误。

### CREATE OR REPLACE MATERIALIZED VIEW

`CREATE OR REPLACE MATERIALIZED VIEW` 会以原子方式替换现有的 materialized view 及其内部存储表 (如有) 。此操作要求使用 `Atomic` 或 `Replicated` 数据库引擎。

```sql theme={null}
CREATE OR REPLACE MATERIALIZED VIEW [db.]name [ON CLUSTER cluster]
[TO [db.]target_table]
[ENGINE = engine]
[POPULATE]
[REFRESH ...]
AS SELECT ...
```

关键行为：

* **不带 `TO` 子句**：旧的内部表会被删除，并创建一个新的内部表。除非指定了 `POPULATE`，否则内部表中的现有数据将会丢失。
* **带 `TO` 子句**：仅替换视图定义；目标表及其数据不受影响。
* 兼容 `REFRESH`、`ON CLUSTER` 和所有引擎选项。`POPULATE` 仅在 `Atomic` 数据库中受支持——在 `Replicated` 数据库中会被拒绝 (请参见下方关于 `POPULATE` 的说明) 。
* 需要 `CREATE VIEW` 和 `DROP VIEW` 特权。

<Note>
  `CREATE OR REPLACE MATERIALIZED VIEW` 仅受 `Atomic` 或 `Replicated` 数据库引擎支持。不支持 `Ordinary` 数据库引擎。
</Note>

**示例：**

```sql theme={null}
-- Create a materialized view with an inner table
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    AS SELECT x, sum(y) AS total FROM src GROUP BY x;

-- Replace with a new definition (old inner table data is lost)
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    AS SELECT x, count() AS cnt FROM src GROUP BY x;

-- Replace with POPULATE to backfill from existing source data
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    POPULATE
    AS SELECT x FROM src;

-- Replace an inner-table MV with a TO-table MV (target data is preserved)
CREATE OR REPLACE MATERIALIZED VIEW mv TO target
    AS SELECT x FROM src;
```

<Tip>
  以下是使用 [Materialized views](/zh/concepts/features/materialized-views/cascading-materialized-views) 的分步指南。
</Tip>

Materialized views 用于存储由相应 [SELECT](/zh/reference/statements/select/index) 查询转换后的数据。

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

创建带有 `TO [db].[table]` 的 Materialized view 时，也可以使用 `POPULATE` 从现有源数据回填目标表 (目标表可能已包含数据，此时回填的行会被追加) 。`POPULATE` 不能与 `REFRESH` 结合使用：可刷新的 [可刷新materialized view](#refreshable-materialized-view) 会在首次刷新时填充，因此 `POPULATE` 会将初始数据加载两次 (请改用 `EMPTY` 跳过首次刷新) 。

Materialized view 的实现方式如下：当向 `SELECT` 中指定的表插入数据时，部分新插入的数据会经过该 `SELECT` 查询转换，结果再插入到该视图中。

<Note>
  ClickHouse 中的 materialized view 在插入到目标表时使用**列名**，而不是列顺序。如果 `SELECT` 查询结果中缺少某些列名，ClickHouse 会使用默认值，即使该列不是 [Nullable](/zh/reference/data-types/nullable)。安全的做法是在使用 materialized view 时为每一列都添加别名。

  ClickHouse 中的 materialized view 在实现上更像插入触发器。如果视图查询中包含聚合，它只会应用于刚插入的那一批数据。对源表现有数据的任何更改 (如 update、delete、drop partition 等) 都不会改变 materialized view。

  ClickHouse 中的 materialized view 在发生错误时不具有确定性行为。这意味着，已经写入的块会保留在目标表中，但出错后的所有块都不会保留。

  默认情况下，如果向某个视图写入时抛出异常，`INSERT` 查询将失败。此时数据块是否已经到达源表并不保证——这取决于插入管道的时序，而不是视图错误。对失败的 `INSERT` 使用插入去重 (`insert_deduplicate`、`deduplicate_blocks_in_dependent_materialized_views`) 进行重试，以获得对源表及所有依赖视图的 exactly-once 交付。

  在 `INSERT` 查询上设置 `materialized_views_ignore_errors=true` 只会改变错误报告方式：每个视图错误都会作为警告记录下来，且 `INSERT` 查询会成功。向失败视图的目标端交付是不完整的——异常发生前已处理的块会被保留，而失败的块及其后的所有块都会从该视图中丢弃。该目标端下游的视图只能看到实际到达的那些块，因此它们的交付也同样是不完整的。未抛出异常的同级视图 (以及它们的下游链) 会被完整写入，而源表也会照常写入。由于 `INSERT` 会报告成功，客户端不会收到失败信号，也不会触发自动重试；仅当源表写入绝不能被视图侧问题阻塞时，才应使用此设置 (例如 `system.*_log` 表) 。

  对于 `system.*_log` 表，`materialized_views_ignore_errors` 默认为 `true`。
</Note>

如果指定 `POPULATE`，则在创建视图时，现有源表数据会被插入到视图中。否则，视图只包含在视图创建后插入到源表中的数据。

对于普通的 `CREATE MATERIALIZED VIEW`，`POPULATE` 默认具有**原子性** (设置 `materialized_views_populate_atomically = 1`) ：在源表上短暂持有排他锁的同时，视图会订阅源表的新插入，并对现有数据创建快照，因此在填充期间并发插入的每一行都会被**恰好一次**交付到视图中——既不会遗漏，也不会重复。随后，填充过程 (可能耗时较长) 会读取固定快照，而不持有任何锁。

这是本地插入路径的原子性：排他锁仅会与在**同一服务器**上获取此源表存储锁的插入操作串行化，因此 exactly-once 保证仅覆盖通过此服务器到达的插入。这并非集群范围的保证——在填充期间并发插入到 `ReplicatedMergeTree` 源表的另一副本上的行，或通过分布式写入路径 (例如插入到 `Distributed` 表或通过 `ON CLUSTER`) 插入的行，不在此范围内，仍可能被遗漏或重复。

如果填充失败——例如，无法在 `lock_acquire_timeout` 内获取繁忙源表上的排他锁，或者视图的 `SELECT` 在运行时抛出异常——则刚创建的视图会被删除，且 `CREATE` 查询会失败，不会留下其创建的任何内容，因此可以直接重试。对于 `TO [db].[table]` 形式，此回滚仅删除视图，绝不会删除预先存在的目标表——但失败的填充已插入目标表的行会保留在其中，就像向该表执行失败的 `INSERT ... SELECT` 后一样，因此重试 `CREATE` 会再次插入这些行。如果回填必须精确无误，请重试插入到已截断或全新的目标表中，或者使用如 `ReplacingMergeTree` 这样的去重引擎。

<Note>
  原子性要求源表支持读取固定的时间点快照——即 `MergeTree` 家族和 `Memory`。对于任何其他源 (视图、`Distributed`、`Merge`、`Buffer`、`Log` 家族，或不在 `Atomic` database 中的表)，填充会回退到旧版非原子行为 (记录在 server log 中)：现有数据会通过单独且未协调的快照读取，因此在填充期间插入的行可能会遗漏或重复。在这种情况下，如果需要精确数据，请创建视图并单独运行 `INSERT ... SELECT`。设置 `materialized_views_populate_atomically = 0` 会对所有源强制使用此旧版行为。

  原子填充仅适用于普通的 `CREATE MATERIALIZED VIEW`。`CREATE OR REPLACE` / `REPLACE MATERIALIZED VIEW ... POPULATE` 始终使用旧版非原子填充。

  `POPULATE` 不支持 `Replicated` databases (使用 `database_replicated_allow_heavy_create` 覆盖此限制)，且不支持 ClickHouse Cloud。通过该覆盖启用时，填充始终为旧版非原子填充——失败的填充无法在所有副本上一致地回滚。
</Note>

`SELECT` 查询可以包含 `DISTINCT`、`GROUP BY`、`ORDER BY`、`LIMIT`。请注意，相应的转换会在每个插入数据块上独立执行。例如，如果设置了 `GROUP BY`，数据会在插入期间进行 聚合，但仅限于单个插入数据包内的数据，之后不会再进一步聚合。例外情况是使用可自行执行数据 聚合 的 `ENGINE`，例如 `SummingMergeTree`。

如果 materialized view 使用 `TO [db.]name` 这种写法，你可以先 `DETACH` 该视图，对目标表执行 `ALTER`，然后再 `ATTACH` 之前已 `DETACH` 的视图。

视图看起来与普通表相同。例如，它们会显示在 `SHOW TABLES` 查询结果中。

要删除视图，请使用 [DROP VIEW](/zh/reference/statements/drop#drop-view)。不过，`DROP TABLE` 对 VIEW 也同样适用。

## SQL 安全

`DEFINER` 和 `SQL SECURITY` 允许你指定在执行视图的底层查询时使用哪个 ClickHouse 用户。
`SQL SECURITY` 有三个合法取值：`DEFINER`、`INVOKER` 或 `NONE`。你可以在 `DEFINER` 子句中指定任何现有用户或 `CURRENT_USER`。

下表说明了从视图中查询时，哪个用户需要具备哪些权限。
请注意，无论 SQL 安全选项是什么，在所有情况下，仍然需要具备 `GRANT SELECT ON <view>` 才能读取该视图。

| SQL 安全选项 | 视图 | Materialized View |
| - | - | - |
| `DEFINER alice` | `alice` 必须拥有该视图源表的 `SELECT` 权限。 | `alice` 必须拥有该视图源表的 `SELECT` 权限，以及该视图目标表的 `INSERT` 权限。 |
| `INVOKER` | 用户必须拥有该视图源表的 `SELECT` 权限。 | 不能为 materialized view 指定 `SQL SECURITY INVOKER`。 |
| `NONE` | - | - |

<Note>
  `SQL SECURITY NONE` 是一个已弃用选项。任何有权创建带有 `SQL SECURITY NONE` 的视图的用户，都能够执行任意查询。
  因此，要使用此选项创建视图，必须具备 `GRANT ALLOW SQL SECURITY NONE TO <user>`。
</Note>

如果未指定 `DEFINER`/`SQL SECURITY`，结果取决于 [`ignore_empty_sql_security_in_create_view_query`](/zh/reference/settings/server-settings/settings/other#ignore_empty_sql_security_in_create_view_query) 服务器设置。

当其默认值为 `true` 时，查询会按原样存储，且视图会获得空的 SQL 安全类型。普通视图随后将使用调用者的权限运行；对于显式指定目标表的 materialized view，将跳过对该目标表的访问检查：向源表插入数据不需要目标表的 `INSERT` 权限，从视图中读取数据也不需要其 `SELECT` 权限。

当值为 `false` 时，会在创建时将以下默认值写入视图定义：

* `SQL SECURITY`：普通视图为 `INVOKER` (可通过 [`default_normal_view_sql_security`](/zh/reference/settings/session-settings/default#default_normal_view_sql_security) 配置) ，materialized view 为 `DEFINER` (可通过 [`default_materialized_view_sql_security`](/zh/reference/settings/session-settings/default#default_materialized_view_sql_security) 配置)
* `DEFINER`：`CURRENT_USER` (可通过 [`default_view_definer`](/zh/reference/settings/session-settings/default#default_view_definer) 配置)

无论该设置为何值，可刷新materialized view 始终会获得这些默认值。

视图在服务器启动时附加或重新加载时，会保留其存储定义中的 SQL 安全类型，因此，未指定 `DEFINER`/`SQL SECURITY` 而存储的视图会保留空的 SQL 安全类型。

要更改现有视图的 SQL 安全设置，请使用

```sql theme={null}
ALTER TABLE MODIFY SQL SECURITY { DEFINER | INVOKER | NONE } [DEFINER = { user | CURRENT_USER }]
```

### 示例

```sql theme={null}
CREATE VIEW test_view
DEFINER = alice SQL SECURITY DEFINER
AS SELECT ...
```

```sql theme={null}
CREATE VIEW test_view
SQL SECURITY INVOKER
AS SELECT ...
```

## Live View

<DeprecatedBadge />

该功能已弃用，后续将被移除。

为方便查阅，旧版文档见[此处](https://pastila.nl/?00f32652/fdf07272a7b54bda7e13b919264e449f.md)

## 可刷新materialized view

```sql theme={null}
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
REFRESH [EVERY|AFTER interval [OFFSET interval]]
[RANDOMIZE FOR interval]
[DEPENDS ON [db.]name [, [db.]name [, ...]]]
[SETTINGS name = value [, name = value [, ...]]]
[APPEND [INCREMENTAL]]
[TO[db.]name] [(columns)] [ENGINE = engine]
[EMPTY]
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | NONE }]
AS SELECT ...
[COMMENT 'comment']
```

其中，`interval` 是由若干简单时间间隔构成的序列：

```sql theme={null}
number SECOND|MINUTE|HOUR|DAY|WEEK|MONTH|YEAR
```

`REFRESH` 子句必须至少指定 `EVERY`、`AFTER` 或 `DEPENDS ON` 之一。单独使用 `REFRESH` (即一个都不带) 会被拒绝。不带 `EVERY`/`AFTER` 的 `REFRESH DEPENDS ON ...` 是 `REFRESH AFTER 0 SECOND DEPENDS ON ...` 的简写；请参见下方的 [刷新依赖关系](#refresh-dependencies)。

周期性运行相应的查询，并将其结果存储到表中。

* 如果指定了 `APPEND`，每次刷新都会将行插入表中，而不会删除现有行。该 insert 不是原子的，和普通的 `INSERT INTO ... SELECT` 查询一样。
* 如果指定了 `APPEND INCREMENTAL`，每次刷新只会对自上次刷新以来已提交到源表的行运行查询，并追加其结果。
* 否则，每次刷新都会以原子方式替换表'的现有内容。

与常规不可刷新的 materialized view 的区别：

* 没有插入触发器。当新数据插入到 `SELECT` 中指定的表时，它*不会*自动推送到可刷新materialized view。相反，只有在周期性刷新或手动刷新执行时才会插入数据。
* 对 `SELECT` 查询没有限制。表函数 (例如 `url()`) 、视图、UNION、JOIN 都允许使用。`APPEND INCREMENTAL` 是唯一的例外：它要求源表是单个普通 `MergeTree` 表，并且设置了 `enable_block_number_column = 1` 和 `enable_block_offset_column = 1`，同时不允许 `JOIN`、`UNION`、子查询、视图和表函数。

<Note>
  查询中 `REFRESH ... SETTINGS` 部分的 settings 是刷新设置 (例如 `refresh_retries`) ，与常规设置 (例如 `max_threads`) 不同。常规设置可以在查询末尾使用 `SETTINGS` 指定。
</Note>

### 刷新调度

刷新调度示例：

```sql theme={null}
REFRESH EVERY 1 DAY -- every day, at midnight (UTC)
REFRESH EVERY 1 MONTH -- on 1st day of every month, at midnight
REFRESH EVERY 1 MONTH OFFSET 5 DAY 2 HOUR -- on 6th day of every month, at 2:00 am
REFRESH EVERY 2 WEEK OFFSET 5 DAY 15 HOUR 10 MINUTE -- every other Saturday, at 3:10 pm
REFRESH EVERY 30 MINUTE -- at 00:00, 00:30, 01:00, 01:30, etc
REFRESH AFTER 30 MINUTE -- 30 minutes after the previous refresh completes, no alignment with time of day
-- REFRESH AFTER 1 HOUR OFFSET 1 MINUTE -- syntax error, OFFSET is not allowed with AFTER
REFRESH EVERY 1 WEEK 2 DAYS -- every 9 days, not on any particular day of the week or month;
                            -- specifically, when day number (since 1969-12-29) is divisible by 9
REFRESH EVERY 5 MONTHS -- every 5 months, different months each year (as 12 is not divisible by 5);
                       -- specifically, when month number (since 1970-01) is divisible by 5
```

`RANDOMIZE FOR` 会随机调整每次刷新的时间，例如：

```sql theme={null}
REFRESH EVERY 1 DAY OFFSET 2 HOUR RANDOMIZE FOR 1 HOUR -- every day at random time between 01:30 and 02:30
```

对于给定视图，任一时刻最多只能有一个 刷新 在运行。例如，如果一个带有 `REFRESH EVERY 1 MINUTE` 的视图完成一次 刷新 需要 2 分钟，那么它实际上就只会每 2 分钟 刷新 一次。如果之后它变快了，能在 10 秒内完成 刷新，那么它又会恢复为每分钟 刷新 一次。 (特别地，它不会为了补上错过的 刷新 而改为每 10 秒 刷新 一次——因为并不存在这样的积压。)

通常，materialized view 创建后会立即启动第一次 刷新：距离上次 刷新 的时间相当于无穷大，因此无论什么调度，都会认为现在应该刷新。如果指定了 `EMPTY`，则会跳过这次初始 刷新，第一次 刷新 会在下一个计划时间发生；例如，对于 `EVERY 1 HOUR`，第一次 刷新 会在当前小时结束时发生。

### 在 Replicated DB 中

如果可刷新 materialized view 位于 [Replicated database](/zh/reference/engines/database-engines/replicated) 中，各副本会相互协调，从而确保在每个计划的刷新时间点只有一个副本执行刷新。这里要求使用 [ReplicatedMergeTree](/zh/reference/engines/table-engines/mergetree-family/replication) 表引擎，这样所有副本都能看到刷新生成的数据。

在 `APPEND` 模式下，可以通过 `SETTINGS all_replicas = 1` 禁用协调。这样一来，各副本会彼此独立地执行刷新。在这种情况下，不要求使用 ReplicatedMergeTree。

在非 `APPEND` 模式下，仅支持协调刷新。若要使用非协调方式，请使用 `Atomic` database 和 `CREATE ... ON CLUSTER` 查询，在所有副本上创建可刷新 materialized view。

协调通过 Keeper 完成。znode 路径由 [default\_replica\_path](/zh/reference/settings/server-settings/settings/default-replica#default_replica_path) 服务器设置决定。

### 刷新依赖关系

`DEPENDS ON` 用于同步不同表的刷新：

```sql theme={null}
CREATE MATERIALIZED VIEW dependent REFRESH EVERY 1 HOUR DEPENDS ON dependency [...]
```

只有在其所有依赖视图都完成刷新后，从属视图才会开始刷新。

若要在另一个视图刷新后立即触发刷新：

```sql theme={null}
CREATE MATERIALIZED VIEW dependent REFRESH AFTER 0 SECOND DEPENDS ON dependency [...]
```

或者，等价地说：

```sql theme={null}
CREATE MATERIALIZED VIEW dependent REFRESH DEPENDS ON dependency [...]
```

<Note>
  `DEPENDS ON` 仅适用于可刷新materialized view 之间。特别是，如果依赖视图使用了 `TO <table>`，请务必使用视图名称而不是表名称。如果 `DEPENDS ON` 列表中包含普通表、不可刷新的视图，或者存在拼写错误，该视图将永远不会刷新，并会在 `system.view_refreshes` 中显示状态 `MissingDependencies`。可以使用 `ALTER` 更改或移除依赖关系，参见 [更改刷新参数](#changing-refresh-parameters)。
</Note>

#### 使用 DEPENDS ON 保持一致的传播延迟

如果两个视图都使用相同周期的 `REFRESH EVERY`，那么该依赖关系会在每个时间槽内生效。

例如，假设视图 X 和 Y 都使用 `REFRESH EVERY 1 HOUR`，且 Y 从 X 的输出表中读取数据。在没有依赖关系时，Y 通常会看到 X 上一小时刷新生成的数据。使用 `DEPENDS ON X` 后，Y 在 11:00 的刷新只有在 X 于 11:00 的刷新完成后才会开始。

```text theme={null}
           10:00            11:00            12:00
           │                │                │
  X:        [run]┐           [run]┐           [run]┐
                 │                │                │
  Y:             └►[run]          └►[run]          └►[run]
```

如果刷新执行时间超过刷新周期，依赖项和依赖方都可能各自独立地跳过某些时间槽。无法保证依赖方会对依赖项的每一次刷新都恰好执行一次刷新。

```text theme={null}
           10:00          11:00          12:00          13:00
           │              │              │              |
  X:        [run]┐         [run]┐         [run]┐         [run]┐
                 │              └────┐    (Y skips 12:00)     └───┐
  Y:             └►[10:00 ru------un]└►[11:00 ru---------------un]└►[13:00 run]
```

#### 使用 DEPENDS ON 进行批次流处理

如果未使用 `REFRESH EVERY`，则依赖视图 X 会在其所有依赖项自 X 上次刷新以来都至少刷新过一次后刷新。`REFRESH AFTER T` 会引入一个延迟：依赖视图会在依赖项完成刷新后的 T 时间开始刷新。

允许循环依赖，而且这很有用。请考虑下面这个由可刷新materialized view 构成的关系图：

1. X 从某个 stream 中取出一批行，并将其写入一个表。
2. 然后，Y 和 Z 都从该表读取数据，执行不同的聚合，并将结果追加到其他表中。
3. 该批次完全处理完后，X 会取出下一批次，然后循环重复进行。

```text theme={null}
            source
               │
               ▼
          ┌─────────┐
     ┌───►│    X    │◄───┐
     │    └──┬───┬──┘    │
  DEPENDS    │   │    DEPENDS
    ON       ▼   ▼      ON
     │      ┌─┐ ┌─┐      │
     └──────┤Y│ │Z├──────┘
            └─┘ └─┘
```

完整示例：

```sql theme={null}
CREATE TABLE current_batch (t UInt64, v Int64) ENGINE ReplicatedMergeTree ORDER BY t;
CREATE TABLE batch_log (max_t UInt64, n Int64, v_sum Int64, processed_at DateTime64) ENGINE ReplicatedMergeTree ORDER BY max_t;
CREATE TABLE stats (h UInt64, n UInt64) ENGINE ReplicatedSummingMergeTree ORDER BY h;

-- (system.numbers stands in for a data source with monotonically increasing timestamps or sequence numbers)
CREATE MATERIALIZED VIEW current_batch_v REFRESH EVERY 10 SECOND DEPENDS ON batch_log_v, stats_v TO current_batch AS SELECT number as t, number * 10 as v FROM system.numbers WHERE number > (SELECT max(max_t) FROM batch_log) LIMIT 100;

CREATE MATERIALIZED VIEW batch_log_v REFRESH DEPENDS ON current_batch_v APPEND TO batch_log AS SELECT max(t) as max_t, count() as n, sum(v) as v_sum, now64() as processed_at FROM current_batch;

CREATE MATERIALIZED VIEW stats_v REFRESH DEPENDS ON current_batch_v APPEND TO stats AS SELECT cityHash64(v) % 20 as h, count() as n FROM current_batch GROUP BY h;

-- Must trigger initial refresh manually.
SYSTEM REFRESH VIEW current_batch_v;
```

更长的事件链也同样适用。

只有在启用刷新协调时，这种方式才能正常工作，也就是说，这些视图位于 Replicated 或 Shared database 中。如果没有协调机制，服务器重启会中断这一循环，因此每次重启后都需要手动执行一次 `SYSTEM REFRESH VIEW`，而不是只在创建视图后执行一次。

### 刷新设置

可用的刷新设置：

* `refresh_retries` - 刷新查询因异常失败时的重试次数。如果所有重试都失败，则跳过并等待下一个计划刷新时间。0 表示不重试，-1 表示无限重试。默认值：2。
* `refresh_retry_initial_backoff_ms` - 如果 `refresh_retries` 不为 0，首次重试前的延迟时间。此后每次重试的延迟都会翻倍，直到达到 `refresh_retry_max_backoff_ms`。默认值：100 毫秒。
* `refresh_retry_max_backoff_ms` - 刷新尝试之间延迟时间指数增长的上限。默认值：60000 毫秒 (1 分钟) 。
* `all_replicas` - 在带有 `APPEND` 的 [Replicated database](/zh/reference/engines/database-engines/replicated) 中，控制是让所有副本独立刷新，还是在每个计划时间点仅由一个副本刷新。视图创建后不可更改。默认值：`false`。

### 更改刷新参数

可使用 [`ALTER TABLE ... MODIFY REFRESH`](/zh/reference/statements/alter/view#alter-table--modify-refresh-statement) 更改现有可刷新materialized view的刷新参数：

```sql theme={null}
ALTER TABLE [db.]name MODIFY REFRESH EVERY|AFTER ... [RANDOMIZE FOR ...] [DEPENDS ON ...] [SETTINGS ...]
```

调度 (`EVERY` 或 `AFTER`) 是必填项：该语句始终会用指定内容替换*所有*刷新参数——包括调度、`RANDOMIZE FOR`、`DEPENDS ON` 和刷新设置。凡是未指定的内容，都会重置为默认值 (设置) 或被移除 (依赖项、随机化) 。

<Note>
  * 如果只想修改刷新设置 (例如 `refresh_retries`) ，请重复现有调度：

    ```sql theme={null}
    ALTER TABLE rmv MODIFY REFRESH EVERY 1 HOUR SETTINGS refresh_retries = 5;
    ```

  * materialized view 不支持 `ALTER TABLE ... MODIFY SETTING refresh_retries = ...`；必须通过 `MODIFY REFRESH` 进行修改。

  * 不支持更改刷新模式：`APPEND` 和 `INCREMENTAL` 既不能添加也不能移除。

  * `all_replicas` 设置在创建后无法更改。
</Note>

示例：

```sql theme={null}
-- Change the schedule, drop existing settings and dependencies.
ALTER TABLE rmv MODIFY REFRESH EVERY 30 MINUTE;

-- Change the schedule and tune retry behavior.
ALTER TABLE rmv MODIFY REFRESH EVERY 30 MINUTE
SETTINGS refresh_retries = 5,
         refresh_retry_initial_backoff_ms = 500,
         refresh_retry_max_backoff_ms = 60000;

-- Keep the dependency while changing the period.
ALTER TABLE rmv MODIFY REFRESH EVERY 6 HOUR DEPENDS ON other_rmv;

-- Drop the dependency by omitting `DEPENDS ON`.
ALTER TABLE rmv MODIFY REFRESH EVERY 6 HOUR;
```

### 其他操作

所有可刷新materialized view 的状态都可在表 [`system.view_refreshes`](/zh/reference/system-tables/view_refreshes) 中查看。具体而言，其中包含刷新进度 (如果正在运行) 、上次和下次刷新时间，以及刷新失败时的异常信息。

如需手动停止、启动、触发或取消刷新，请使用 [`SYSTEM STOP|START|REFRESH|WAIT|CANCEL VIEW`](/zh/reference/statements/system#managing-refreshable-materialized-views)。

如需等待刷新完成，请使用 [`SYSTEM WAIT VIEW`](/zh/reference/statements/system#wait-view)。这在创建视图后等待首次刷新时尤其有用。

<Note>
  顺带一提：刷新查询可以从正在刷新的视图中读取数据，并看到刷新前版本的数据。这意味着你可以实现 Conway's game of life：[https://pastila.nl/?00021a4b/d6156ff819c83d490ad2dcec05676865#O0LGWTO7maUQIA4AcGUtlA==](https://pastila.nl/?00021a4b/d6156ff819c83d490ad2dcec05676865#O0LGWTO7maUQIA4AcGUtlA==)
</Note>

## 相关内容

* 博客：[在 ClickHouse 中使用时间序列数据](https://clickhouse.com/blog/working-with-time-series-data-and-functions-ClickHouse)
* 博客：[使用 ClickHouse 构建可观测性解决方案 (第 2 部分) ：链路追踪](https://clickhouse.com/blog/storing-traces-and-spans-open-telemetry-in-clickhouse)

## 临时视图

ClickHouse 支持**临时视图**，其特性如下 (在适用情况下与临时表一致) ：

* **会话生命周期**
  临时视图仅在当前会话期间存在。会话结束后会自动删除。

* **无数据库**
  **不能**使用数据库名限定临时视图。它存在于数据库之外 (会话命名空间) 。

* **不复制 / 不支持 ON CLUSTER**
  临时对象仅在当前会话内有效，**不能**通过 `ON CLUSTER` 创建。

* **名称解析**
  如果临时对象 (表或视图) 与持久对象同名，且某个查询在**不带**数据库名的情况下引用该名称，则会使用**临时**对象。

* **逻辑对象 (无存储) **
  临时视图仅存储其 `SELECT` 文本 (内部使用 `View` 存储) 。它不会持久化数据，也不接受 `INSERT`。

* **Engine 子句**
  **无需**指定 `ENGINE`；如果写为 `ENGINE = View`，也会被忽略，并视为相同的逻辑视图。

* **安全 / 特权**
  创建临时视图需要 `CREATE TEMPORARY VIEW` 特权，而 `CREATE VIEW` 会隐式授予该特权。

* **SHOW CREATE**
  使用 `SHOW CREATE TEMPORARY VIEW view_name;` 可输出临时视图的 DDL。

### 语法

```sql theme={null}
CREATE TEMPORARY VIEW [IF NOT EXISTS] view_name AS <select_query>
```

`OR REPLACE` **不**支持用于临时视图 (与临时表一致) 。如果你需要“替换”临时视图，请先将其删除，再重新创建。

### 示例

创建一个临时源表，并基于它创建一个临时视图：

```sql theme={null}
CREATE TEMPORARY TABLE t_src (id UInt32, val String);
INSERT INTO t_src VALUES (1, 'a'), (2, 'b');

CREATE TEMPORARY VIEW tview AS
SELECT id, upper(val) AS u
FROM t_src
WHERE id <= 2;

SELECT * FROM tview ORDER BY id;
```

查看其 DDL：

```sql theme={null}
SHOW CREATE TEMPORARY VIEW tview;
```

将其删除：

```sql theme={null}
DROP TEMPORARY VIEW IF EXISTS tview;  -- 临时视图使用 TEMPORARY TABLE 语法进行删除
```

### 不允许的用法 / 限制

* `CREATE OR REPLACE TEMPORARY VIEW ...` → **不允许** (请使用 `DROP` + `CREATE`) 。
* `CREATE TEMPORARY MATERIALIZED VIEW ...` → **不允许**。
* `CREATE TEMPORARY VIEW db.view AS ...` → **不允许** (不支持数据库限定) 。
* `CREATE TEMPORARY VIEW view ON CLUSTER 'name' AS ...` → **不允许** (临时对象仅在当前会话内有效) 。
* `POPULATE`、`REFRESH`、`TO [db.table]`、内部引擎以及所有 MV 专用子句 → **不适用于**临时视图。

### 关于分布式查询的说明

临时**视图**只是一个定义，本身不包含可传递的数据。如果临时视图引用了临时**表** (例如 `Memory`) ，其数据在分布式查询执行期间也可以像临时表一样传送到远程服务器。

#### 示例

```sql theme={null}
-- A session-scoped, in-memory table
CREATE TEMPORARY TABLE temp_ids (id UInt64) ENGINE = Memory;

INSERT INTO temp_ids VALUES (1), (5), (42);

-- A session-scoped view over the temp table (purely logical)
CREATE TEMPORARY VIEW v_ids AS
SELECT id FROM temp_ids;

-- Replace 'test' with your cluster name.
-- GLOBAL JOIN forces ClickHouse to *ship* the small join-side (temp_ids via v_ids)
-- to every remote server that executes the left side.
SELECT count()
FROM cluster('test', system.numbers) AS n
GLOBAL ANY INNER JOIN v_ids USING (id)
WHERE n.number < 100;

```
