创建动态表¶
本主题概述了创建动态表的关键概念。
开始之前,请确保您 有权创建动态表,并且动态表查询使用的所有对象都启用了 更改跟踪。
创建动态表时可能会有一些限制。有关完整列表,请参阅 动态表限制。
启用更改跟踪¶
在创建具有增量刷新模式的动态表时,如果尚未对其查询的表启用更改跟踪,Snowflake 会自动尝试对其启用更改跟踪。为支持增量刷新,必须对动态表使用的所有基础对象启用 非零 Time Travel 保留 的更改跟踪。
随着基础对象的变化,动态表也会发生变化。如果您重新创建一个基础对象,您必须重新启用更改跟踪。
备注
Snowflake 不会自动尝试在使用完全刷新模式创建的动态表上启用更改跟踪。
要对特定数据库对象启用更改跟踪,请在该对象上使用 ALTER TABLE、ALTER VIEW 和类似命令。创建动态表的用户必须具有 OWNERSHIP 权限,才能对所有基础对象启用更改跟踪。
要检查是否启用了更改跟踪,请在基础对象上使用 SHOW VIEWS、SHOW TABLES 和类似命令,并检查 change_tracking
列。
支持的基础对象¶
动态表支持以下基础对象:
表
Snowflake 管理的 Apache Iceberg™ 表
外部管理的 Apache Iceberg™ 表
示例:创建简单的动态表¶
假设您要创建一个动态表,该表包含来自名称为 staging_table
的表的 product_id
和 product_name
列,并且您决定:
您希望动态表中的数据最多比
staging_table
中的数据滞后 20 分钟。您希望将仓库
mywh
用于 刷新 所需的计算资源。您希望自动选择刷新模式。
Snowflake 建议仅在开发期间使用自动刷新模式。有关更多信息,请参阅 选择动态表刷新模式的最佳实践。
您希望动态表在创建时同步刷新。
您希望自动选择刷新模式,并希望动态表在创建时同步刷新。
要创建此动态表,请执行以下 CREATE DYNAMIC TABLE SQL 语句:
CREATE OR REPLACE DYNAMIC TABLE my_dynamic_table
TARGET_LAG = '20 minutes'
WAREHOUSE = mywh
REFRESH_MODE = auto
INITIALIZE = on_create
AS
SELECT product_id, product_name FROM staging_table;
有关参数和变体语法的完整列表,请参阅 CREATE DYNAMIC TABLE 参考。
从 Snowflake 管理或外部管理的 Apache Iceberg™ 表创建动态表¶
从 Iceberg 表创建动态表与从普通表创建类似。可以像为普通表创建动态表一样,执行 CREATE DYNAMIC TABLE SQL 语句,将 Snowflake 管理的表或外部目录管理的表作为基础对象。
如果动态表以 Snowflake 管理的 Iceberg 表作为基表,则在以下情况下非常有用:希望您的数据管道操作基于 Snowflake 管理的 Iceberg 表中的数据,或者希望管道操作由其他引擎写入的 Iceberg 表。需要注意的是,外部引擎无法写入 Snowflake 管理的 Iceberg 表;对于 Snowflake 来说,它们是可读写的,而对于外部引擎则是只读的。
如果动态表以 外部(非 Snowflake)目录 管理的 Iceberg 表为基础(例如 AWS 管理、由 Apache Spark 等引擎写入),则适用于处理外部数据湖中的数据。您可以在外部管理的数据之上创建动态表,并在 Snowflake 中持续处理数据,而无需复制或导入数据。
使用 Iceberg 表的限制和注意事项¶
常规动态表 和 动态 Iceberg 表 的所有限制仍然适用。
此外:
Iceberg 基表的所有限制均适用。有关更多信息,请参阅 注意事项和限制。
您可以创建动态表,读取 Snowflake 原生表、Snowflake 管理的 Iceberg 表以及外部管理的 Iceberg 表。
对于外部管理的 Iceberg 基表,动态表以文件级别跟踪变化,而其他基表以行级别跟踪变化。在外部管理的 Iceberg 表上频繁执行写入复制操作(例如更新或删除)可能会影响增量刷新性能。
使用不可变性约束创建动态表¶
不可变性约束让您可以更精细地控制动态表的更新方式和时间。约束让表的部分数据保持静态,而不是整个表始终反映最新查询结果。
通过将动态表的特定部分标记为不可变,您可以执行以下操作:
防止将更新或删除传播到现有数据。
限制满足条件的行的插入、更新和删除。
在允许其他部分增量更新的同时,限制未来修改。
表的其余部分(即不符合不可变性条件的行)仍可修改,并可在刷新期间更新。
要强制执行不可变性约束,请在运行 CREATE DYNAMIC TABLE 或 ALTER DYNAMIC TABLE 命令时指定 IMMUTABLE WHERE 参数。要查看某行是否可修改,请使用 METADATA$IS_IMMUTABLE
列。例如 SELECT * , METADATA$IS_IMMUTABLE FROM my_dynamic_table
。
在初始刷新期间,IMMUTABLE WHERE 谓词会被忽略,但在后续所有刷新中生效。在完全刷新模式下,它仅对不符合条件的行重新计算。增量刷新模式下的流和动态表可以从这些完全刷新表中读取数据。
不可变性约束使用示例¶
以下示例通过使用 IMMUTABLE WHERE 参数创建一个动态表,并将其设置为不可变。
CREATE DYNAMIC TABLE my_dynamic_table (id1 INT)
TARGET_LAG = '20 minutes'
WAREHOUSE = mywh
IMMUTABLE WHERE ( <expr> )
AS
SELECT id AS id1 FROM staging_table;
在 IMMUTABLE WHERE 条件中引用的列必须是动态表中的列,而不能是基表中的列。例如,上述示例中的 IMMUTABLE WHERE 表达式只能使用 id1
,而不能使用 id
。
一个动态表上只能设置一个 IMMUTABLE WHERE 条件。要替换现有谓词,可以使用 ALTER DYNAMIC TABLE 命令,如下例所示:
-- Set or replace an existing predicate:
ALTER DYNAMIC TABLE my_dynamic_table SET IMMUTABLE WHERE ( <expr> );
-- Remove an existing predicate:
ALTER DYNAMIC TABLE my_dynamic_table UNSET IMMUTABLE;
要查看动态表上的不可变性约束,请运行 SHOW DYNAMIC TABLES 命令。immutable_where
列显示表上设置的 IMMUTABLE WHERE 约束,如果未设置,则显示为 NULL。
有关计算成本的信息,请参阅 计算不变性约束的成本。
示例:使用 IMMUTABLE WHERE 防止在维度表更改时重新计算旧数据¶
在以下示例中,更新维度表中的一行只会导致与其关联的最近一天的事实数据被重新处理,而不是处理所有历史事实数据:
CREATE DYNAMIC TABLE joined_data
TARGET_LAG = '1 minute'
WAREHOUSE = mywh
IMMUTABLE WHERE (timestamp_col < CURRENT_TIMESTAMP() - INTERVAL '1 day')
AS
SELECT F.primary_key primary_key, F.timestamp_col timestamp_col, D.value dim_value
FROM fact_table F
LEFT OUTER JOIN dimension_table D USING (primary_key);
示例:无限保留的动态表与有限保留的基表¶
以下示例创建了一个仅保留最近添加数据的暂存表,以及一个存储所有解析和过滤数据的动态表。
CREATE TABLE staging_data (raw TEXT, ts TIMESTAMP);
CREATE DYNAMIC TABLE parsed_data
TARGET_LAG = '1 minute'
WAREHOUSE = mywh
IMMUTABLE WHERE (ts < CURRENT_TIMESTAMP() - INTERVAL '7 days')
AS
SELECT parse_json(raw):event_id::string event_id, parse_json(raw):name::string name, parse_json(raw):region::string region, ts
FROM staging_data WHERE region = 'US';
-- Delete old staging data using Task
CREATE TASK delete_old_staging_data
WAREHOUSE = mywh
SCHEDULE = '24 hours'
AS
DELETE FROM staging_data WHERE ts < CURRENT_TIMESTAMP() - INTERVAL '30 days';
示例:使用 IMMUTABLE WHERE 的完全刷新¶
假设由于查询构造的限制(例如使用 Python UDTF),动态表必须处于完全刷新模式。虽然这通常会阻止增量处理,但通过使用 IMMUTABLE WHERE 子句定义不可变区域,可以让下游动态表保持增量刷新,从而仍然受益于性能优化,即使上游表由于查询构造限制需要进行完全刷新。
以下示例展示了一个使用 Python UDTF 的动态表,使其无法进行增量化处理:
CREATE FUNCTION my_udtf(x varchar)
RETURNS TABLE (output VARCHAR)
LANGUAGE PYTHON
AS $$ ... $$;
CREATE DYNAMIC TABLE udtf_dt
TARGET_LAG = '1 hour'
WAREHOUSE = mywh
REFRESH_MODE = FULL
IMMUTABLE WHERE (ts < current_timestamp() - interval '1 day')
AS
SELECT ts, data, output, join_key FROM input_table, TABLE(my_udtf(data));
CREATE DYNAMIC TABLE incremental_join_dt
TARGET_LAG = '1 hour'
WAREHOUSE = mywh
REFRESH_MODE = INCREMENTAL
IMMUTABLE WHERE (ts < current_timestamp() - interval '1 day')
AS
SELECT * FROM udtf_dt JOIN dim_table USING (join_key);
设置不可变性约束的限制和注意事项¶
常规动态表的所有限制 仍然适用。
此外:
动态表上只能有一个 IMMUTABLE WHERE 谓词。使用 ALTER DYNAMIC TABLE ... SET IMMUTABLE WHERE 命令设置新的谓词时,会替换任何现有谓词。
IMMUTABLE WHERE 约束不能包含以下内容:
子查询。
非确定性函数,但时间戳函数除外,如 CURRENT_TIMESTAMP()` 或 CURRENT_DATE()。如果使用时间戳函数,必须确保不可变区域随时间增长。例如,
TIMESTAMP_COL < CURRENT_TIMESTAMP()
is allowed butTIMESTAMP_COL > CURRENT_TIMESTAMP()
不允许。用户定义函数或外部函数。
元数据列,例如以
METADATA$
开头的列。聚合函数、窗口函数或非确定性函数的结果列,或通过窗口函数操作符传递的列。在以下示例动态表中,只有
col3
可以在 IMMUTABLE WHERE 谓词中使用:CREATE DYNAMIC TABLE aggregates TARGET_LAG = '1 minute' WAREHOUSE = mywh AS SELECT col1, SUM(col2) AS col2 FROM input_table GROUP BY col3; CREATE DYNAMIC TABLE window_fns TARGET_LAG = '1 minute' WAREHOUSE = mywh AS SELECT col3, SUM(col4) OVER (PARTITION BY col3 ORDER BY col4) AS col2, col5 FROM input_table;
IMMUTABLE WHERE 约束在克隆和复制过程中会复制,且没有任何限制。
使用回填创建动态表¶
回填是一种零拷贝、低成本的操作,使源数据可以立即在动态表中使用。您可以创建一个动态表,并从常规表中回填初始数据,同时仍然为未来更新定义自定义刷新查询。使用不可变性约束时,只有不可变区域会被回填,即使它不再匹配源数据,也保持不变。可变区域则按照动态表的定义和基表照常计算。
只能回填由 IMMUTABLE WHERE 不可变性约束 定义的数据,因为即使回填数据与上游来源不同,也必须保持不变。
回填使用示例¶
以下示例展示了如何从回填数据的表中创建新的动态表。
每个列名必须在回填表中存在,并且数据类型兼容,顺序必须与回填表相同。回填表的表属性和权限不会复制。
如果指定了 Time Travel 参数 AT | BEFORE
,则会在指定时间从回填表中复制数据。
示例:从表的一部分回填¶
以下示例将 my_dynamic_table
的不可变区域从 my_backfill_table
回填,而可变区域则从动态表的定义计算得出。
在此场景下,如果动态表以增量刷新模式创建,重新初始化会删除所有可变行,仅重新填充可变区域。如果动态表以完全刷新模式创建,则会触发完全刷新,效果相同。
CREATE DYNAMIC TABLE my_dynamic_table (day TIMESTAMP, totalSales NUMBER)
IMMUTABLE WHERE (day < '2025-01-01')
BACKFILL FROM my_backfill_table
TARGET_LAG = '20 minutes'
WAREHOUSE = 'mywh'
AS SELECT DATE_TRUNC('day', ts) AS day, sum(price)
FROM my_base_table
GROUP BY day;
示例:使用回填在动态表中恢复或修改数据¶
您无法直接编辑动态表的数据或定义。要恢复或修复数据,请按照以下替代步骤操作:
将动态表克隆为一个常规表。
根据需要修改克隆表。
从已编辑的表回填到新的动态表。
在以下示例中,my_dynamic_table
从销售基表 sales
汇总每日销售数据:
CREATE OR REPLACE TABLE sales(item_id INT, ts TIMESTAMP, sales_price FLOAT);
INSERT INTO sales VALUES (1, '2025-05-01 01:00:00', 10.0), (1, '2025-05-01 02:00:00', 15.0), (1, '2025-05-01 03:00:00', 11.0);
INSERT INTO sales VALUES (1, '2025-05-02 00:00:00', 11.0), (1, '2025-05-02 05:00:00', 13.0);
CREATE DYNAMIC TABLE my_dynamic_table
TARGET_LAG = 'DOWNSTREAM'
WAREHOUSE = mywh
INITIALIZE = on_create
IMMUTABLE WHERE (day <= '2025-05-01')
AS
SELECT item_id, date_trunc('DAY', ts) day, count(sales_price) AS sales_count FROM sales
GROUP BY item_id, day;
SELECT item_id, to_char(day, 'YYYY-MM-DD') AS day, sales_count FROM my_dynamic_table;
+---------+------------+-------------+
| ITEM_ID | DAY | SALES_COUNT |
|---------+------------+-------------|
| 1 | 2025-05-01 | 3 |
| 1 | 2025-05-02 | 2 |
|---------+-------------+------------|
您还可以选择将旧数据归档,以节省存储成本:
DELETE FROM sales WHERE ts < '2025-05-02';
ALTER DYNAMIC TABLE my_dynamic_table REFRESH;
SELECT item_id, to_char(day, 'YYYY-MM-DD') AS day, sales_count FROM my_dynamic_table;
之后,您发现 2025-05-01
的销售数据存在错误,其中 sales_count
应为 2。要修正该问题,请执行以下操作:
将
my_dynamic_table
克隆为常规表:CREATE OR REPLACE TABLE my_dt_clone_table CLONE my_dynamic_table;
更新克隆的表:
UPDATE my_dt_clone_table SET sales_count = 2 WHERE day = '2025-05-01'; SELECT item_id, to_char(day, 'YYYY-MM-DD') AS day, sales_count FROM my_dt_clone_table;
+---------+------------+-------------+ | ITEM_ID | DAY | SALES_COUNT | |---------+------------+-------------| | 1 | 2025-05-01 | 2 | | 1 | 2025-05-02 | 2 | |---------+-------------+------------|
使用已编辑的克隆表作为回填源,重新创建该动态表。
CREATE OR REPLACE DYNAMIC TABLE my_dynamic_table BACKFILL FROM my_dt_clone_table IMMUTABLE WHERE (day <= '2025-05-01') TARGET_LAG = 'DOWNSTREAM' WAREHOUSE = mywh INITIALIZE = on_create AS SELECT item_id, date_trunc('DAY', ts) day, count(sales_price) AS sales_count FROM sales GROUP BY item_id, day;
利用这种方法,您无需修改基表即可在动态表中恢复或更正数据:
SELECT item_id, to_char(day, 'YYYY-MM-DD') AS day, sales_count FROM my_dynamic_table;
+---------+------------+-------------+ | ITEM_ID | DAY | SALES_COUNT | |---------+------------+-------------| | 1 | 2025-05-01 | 2 | | 1 | 2025-05-02 | 2 | |---------+-------------+------------|
示例:使用回填修改动态表的架构¶
您无法直接更改动态表的架构。要更新架构(例如,添加一列),请按照以下步骤操作:
将动态表克隆为一个常规表。以下示例使用基于
sales
创建的my_dynamic_table`(见 :ref:`上文 <label-data-surgery-example>
)。CREATE OR REPLACE TABLE my_dt_clone_table CLONE my_dynamic_table;
修改克隆表的架构:
ALTER TABLE my_dt_clone_table ADD COLUMN sales_avg FLOAT; SELECT item_id, to_char(day, 'YYYY-MM-DD') as DAY, SALES_COUNT, SALES_AVG FROM my_dt_clone_table;
(可选)向新列填充数据。
使用已编辑的克隆表作为回填源,重新创建该动态表。
CREATE OR REPLACE DYNAMIC TABLE my_dynamic_table BACKFILL FROM my_dt_clone_table IMMUTABLE WHERE (day <= '2025-05-01') TARGET_LAG = 'DOWNSTREAM' WAREHOUSE = mywh INITIALIZE = on_create AS SELECT item_id, date_trunc('DAY', ts) day, count(sales_price) AS sales_count, avg(sales_price) as sales_avg FROM sales GROUP BY item_id, day;
利用这种方法,您无需修改基表即可在动态表中恢复或更正数据:
SELECT item_id, to_char(day, 'YYYY-MM-DD') as DAY, SALES_COUNT, SALES_AVG, metadata$is_immutable as IMMUTABLE from my_dynamic_table ORDER BY ITEM_ID, DAY;
+---------+------------+-------------+-----------+-----------+ | ITEM_ID | DAY | SALES_COUNT | SALES_AVG | IMMUTABLE | |---------+------------+-------------|-----------|-----------| | 1 | 2025-05-01 | 3 | NULL | TRUE | | 1 | 2025-05-02 | 2 | 12 | FALSE | |---------+-------------+------------+-----------|-----------+
使用回填的限制与注意事项¶
常规动态表的所有限制 以及 不可变性约束的限制 仍然适用。
此外,还适用以下限制与注意事项:
目前仅支持常规表作为回填表。
在新建动态表时,您不能指定策略或标签,因为它们会从回填表中复制。
新建动态表与回填表的群集密钥必须相同。
如果您使用 BACKFILL FROM 创建动态表,则以后无法删除其 IMMUTABLE WHERE 约束。
创建动态表的最佳实践¶
将动态表的管道链接在一起¶
定义新的动态表时,不要定义包含许多嵌套语句的大型动态表,而应使用小型动态表与管道组合的方式。
您可以设置动态表来查询其他动态表。例如,想象这样一个场景:您的数据管道从暂存表中提取数据以更新各种维度表(例如,customer
、product
、date
和 time
)。此外,管道还根据这些维度表中的信息更新汇总 sales
表。通过将维度表配置为查询暂存表,将汇总 sales
表配置为查询维度表,可以创建类似于任务图的级联效果。
在此设置中,仅在维度表刷新成功完成后才能执行汇总 sales
表的刷新。这样可以确保数据的一致性并达到滞后目标。通过自动刷新过程,源表中的任何更改都会在适当的时候触发对所有从属表的刷新。

对复杂任务图使用“控制器”动态表¶
如果您有一个动态表的复杂图表,其中包含许多根节点和叶节点,并且您想使用单个命令对整个任务图执行操作(例如更改滞后、手动刷新、挂起),请执行以下步骤:
将所有动态表的
TARGET_LAG
值设置为DOWNSTREAM
。创建一个“控制器”动态表,从任务图中的所有叶中读取。要确保此控制器不消耗资源,请执行以下语句:
CREATE DYNAMIC TABLE controller TARGET_LAG = <target_lag> WAREHOUSE = <warehouse> AS SELECT 1 A FROM <leaf1>, …, <leafN> LIMIT 0;
使用此控制器控制整个图表。例如:
为任务图设置新的目标滞后。
ALTER DYNAMIC TABLE controller SET TARGET_LAG = <new_target_lag>;手动刷新任务图。
ALTER DYNAMIC TABLE controller REFRESH;
使用瞬态动态表降低存储成本¶
瞬态 动态表可随着时间的推移可靠地维护数据,并支持数据保留期内的 Time Travel,但不保留超过故障安全期的数据。默认情况下,动态表数据在 故障安全 存储中保留七天。
对于具有高刷新吞吐量的动态表,这会显著增加存储消耗。因此,只有当动态表的数据不需要与永久表提供的数据保护和恢复处于相同级别时,才应将其变为瞬态表。
您可以使用 CREATE DYNAMIC TABLE 语句创建瞬态动态表,或将现有动态表克隆到瞬态动态表。
动态表创建故障排除¶
创建动态表时,初始刷新要么按计划 (ON_SCHEDULE
) 进行,要么在创建时立即进行 (ON_CREATE
)。初始数据填充或 初始化 取决于此初始刷新发生的时间。例如,对于 ON_CREATE
,如果初始化触发上游动态表刷新,则可能需要更长时间。
初始化可能需要一些时间,具体取决于扫描的数据量。要查看进度,请执行以下操作:
登录 Snowsight。
在导航菜单中,选择 Monitoring » Query History。
在 Filters 下拉列表中,在 SQL Text 筛选器中输入 CREATE DYNAMIC TABLE,并在 Warehouse 筛选器中输入您的仓库名称。
选择 SQL text 下动态表的查询,并使用 Query Details 和 Query Profile 选项卡跟踪进度。