动态表中的非确定性函数

动态表定义中可以使用

CURRENT_TIMESTAMP()
CURRENT_TIMESTAMP()
RAND()
RAND()
UUID()
UUID()
CURRENT_DATE()
CURRENT_DATE()
等非确定性函数,创建和刷新都不报错,也不会因此退化为全量。问题不在性能,而在结果——它们会造成同一张表内不同行的值不一致,根源是动态表的增量计算机制。

为什么会行间不一致

增量计算只对发生变更的行重新执行 SQL,未变更的行直接保留上次计算的结果。因此:

  • 今天新插入的行,非确定性函数在本次刷新时执行,得到今天的值
  • 上周写入、此后没有变更的行,非确定性函数在上周执行,结果保留上周的值

同一张动态表里,不同行的函数值来自不同时刻的执行,行间数据割裂。

各函数的具体行为

以下是增量刷新场景下各函数的实际行为:

函数无源表变更刷新有变更行(INSERT/UPDATE)具体问题
CURRENT_TIMESTAMP()
CURRENT_TIMESTAMP()
所有行值不变变更行更新为本次刷新时刻,未变更行保留原时刻同表内不同行时间戳不一致,无法表示统一的刷新时间
CURRENT_DATE()
CURRENT_DATE()
所有行值不变变更行更新为今天日期,未变更行保留写入当时的日期跨日后行间日期分裂
RAND()
RAND()
所有行值不变变更行产生新随机值,未变更行保留旧值同列在不同时间写入的值来自不同次执行,无法重现
UUID()
UUID()
所有行值不变变更行产生新 UUID,未变更行保留旧 UUID同一逻辑行 UPDATE 后 UUID 改变,破坏行标识语义

DELETE 行为正常:被删除的源表行在动态表中也会被删除,与非确定性函数无关。

示例:CURRENT_TIMESTAMP() 造成的行间不一致

初始状态(第一次刷新,三行同时写入):

id | val | refresh_ts 1 | 100 | 2026-06-01 08:00:00 ← 第一次刷新时写入 2 | 200 | 2026-06-01 08:00:00 3 | 300 | 2026-06-01 08:00:00

一周后,源表 id=2 的 val 被更新,触发增量刷新:

id | val | refresh_ts 1 | 100 | 2026-06-01 08:00:00 ← 未变更,保留一周前的时间戳 2 | 999 | 2026-06-08 09:30:00 ← 变更行,时间戳更新为本次刷新时刻 3 | 300 | 2026-06-01 08:00:00 ← 未变更,保留一周前的时间戳

refresh_ts
refresh_ts
无法表示"这张表最后一次刷新的时间",也无法用于按刷新批次筛选数据。

全量刷新会把整列重置

上面的行间不一致还不是稳定的。动态表在某些情况下会回退为全量刷新(表刚建好的首次刷新、单次变更量过大、部分算子组合等),全量会重新计算整张表,这一列于是被统一刷成该次全量执行的同一时刻;之后的增量刷新又让它重新分裂。所以这一列既不一致、也不稳定,跨刷新无法复现——这正是不该把非确定性函数写进动态表定义的根本原因。如何判断某次刷新是增量还是全量,见 查看动态表刷新模式

替代方案

如需按时间过滤:使用参数化动态表,通过

SESSION_CONFIGS()['dt.args.bizdate']
SESSION_CONFIGS()['dt.args.bizdate']
传入日期参数,不依赖
CURRENT_DATE()
CURRENT_DATE()
。详见 动态表支持参数化定义

如需记录刷新时间:在下游查询时用

CURRENT_TIMESTAMP()
CURRENT_TIMESTAMP()
打时间戳,不要写入动态表定义中。例如:

-- 不要这样做(动态表定义中) CREATE DYNAMIC TABLE dws_report ... AS SELECT id, val, CURRENT_TIMESTAMP() AS refresh_ts FROM ods_source; -- 应该这样做(在查询动态表时) SELECT id, val, CURRENT_TIMESTAMP() AS query_ts FROM dws_report;

如需唯一标识行:用源表的业务主键而非

UUID()
UUID()
,保证行标识在多次刷新后不变。

相关文档

联系我们
预约咨询
微信咨询
电话咨询
邮件咨询