FLIP-70:Flink SQL计算列设计

FLIP-70:Flink SQL计算列设计

状态

当前状态: 已接受
讨论线索: http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-FLIP-70-Support-Computed-Column-for-Flink-SQL-td33126.html
JIRA: FLINK-14386 - Support computed column for create table statement OPEN
Released: Flink Version
在邮件中进行讨论比在wiki上发表评论的方式更好

动机

在任务FLINK-10232中,我们在新模块flink-sql-parser中引入了CREATE TABLE语法。在设计文档FLINK SQL DDL中,我们建议使用计算列来描述process time的时间属性,因此用户可以创建一个具有process time属性的表,如下所示:

create table T1(
  a int,
  b bigint,
  c varchar,
  d as PROCTIME,
) with (
  'k1' = 'v1',
  'k2' = 'v2'
);

d字段将成为T1表的一个process time属性
除此之外,计算列还有以下作用:

  • 虚拟计算列可以用来简化和统一查询。复杂条件可以定义为计算列,并从表上的多个查询中引用,以确保所有查询都使用完全相同的条件。
  • 存储计算列可以用作一个物化缓存,用于复杂的条件,这些条件在运行时计算成本很高。
  • 计算列可以模拟函数索引:使用计算列定义函数表达式并为其编制索引。这对于处理无法直接索引的类型列(如JSON列)非常有用。
  • 对于存储的计算列,这种方法的缺点是存储两次值;一次作为生成列的值,一次作为索引。
  • 如果计算列已编入索引,优化器将识别与列定义匹配的查询表达式,并在查询执行期间酌情使用该列中的索引(目前尚不支持)。

MS-SQL-2016[1]、MYSQL-5.6[2]和ORACLE-11g[3]中引入了计算列

公开接口

计算列语法
结合MS-SQL-2017[1]和MYSQL-5.6[2]的语法,我们提出计算列语法如下:

col_name AS expr
  [COMMENT 'string']
  • 数据类型可以根据表达式来推断。
  • 此列称为虚拟列,列值不被存储,但在读取数据时会被计算,当他输出时会触发计算,特别是proc time(我们可能会急切地进行一些具体化,请参阅RelTimeIndicatorConverter以了解详细信息)。请参阅“列计算/存储”部分,了解当表用作source/sink时如何持久化虚拟列。
  • 如果将来需要,我们可能会支持STORED/VIRTUAL关键字。
  • 如果用户没有更改来显式指定它,默认关键字将是VIRTUAL。
  • 可空性由表达式决定,不能指定。

限制:

  • 允许使用字面量、用户定义函数和内置函数。
  • 不支持子查询。
  • 它只能引用同一表中定义的列。

InitializerExpressionFactory
InitializerExpressionFactory定义计算列值的生成策略。它将为每个虚拟列生成一个RexNode,可以在逻辑计划中使用它来派生我们想要的值。下面的图表说明了它对select语句的工作原理:
在这里插入图片描述
TableColumn
TableColumn描述表的列定义,包括列名、列数据类型和列策略的定义。

TableSchema
TableSchema描述用于内部和当前连接器的表结构。

提议的变更

计划重写
假设我们有一个名为T2的表,其schema如下:

create table T2(
  a int,
  b as a + 1 virtual,
  c bigint,
  e varchar
) with (
  k1=v1,
  k2=v2
);

读取
如果T2表用作扫描表(source表),T2的列b将在扫描正上方的投影中计算。这意味着,我们将从扫描节点到扫描上方的投影进行等价转换。
在这里插入图片描述
我们将为所有虚拟计算列添加投影表达式,TableSource行类型应排除这些虚拟列。

写入
如果T2表用作插入目标表(sink表),T2列b被完全忽略,因为它是虚拟的(不存储)。
在这里插入图片描述
我们将计算非虚拟列并将其插入到目标表中。TableSink行类型应排除这些虚拟列。
注意:这些计划重写发生在sql-to-rel转换阶段。

列计算和存储

对于scan,从表达式计算虚拟列;从实际表源查询存储列。
对于sink,虚拟列被忽略;存储的列在插入接收器时从表达式计算。

TableSchema变更

我们将扩展TableSchema以支持计算列(我们应该始终在代码中保留一个TableSchema,将来连接器只能看到行类型,而不能看到TableSchema,所以我们不应该担心这个问题)
我们将引入一个名为TableColumn的新结构来保存列名、列数据类型和列策略信息。TableSchema持有TableColumn,而不是原始字段名和字段数据类型。

TableSchema行类型接口

在TableSchema中,我们在推导行类型时总是忽略虚拟列。此行类型对应于source的输出物理类型和sink的输入物理类型。

持久化

我们将把TableSchema的TableColumn信息放入CatalogTable属性中,这些属性将用于持久化。对于每个列表达式,有三个项要持久化:

  • 列名称
  • 列类型
  • SQL样式字符串的列表达式,与用户编写它们的方式类似

反序列化时,我们使用SqlParser将表达式字符串解析为SqlNode,然后将其转换为RexNode并应用投影。

兼容性、弃用和迁移计划

这是一个新功能,与旧版本的Flink兼容。我们可以扩展TableSourceTable以支持计算列接口,但这对用户是透明的。

实施计划

  • 引入InitializerExpressionFactory来处理默认值的初始化和生成的列的计算表达式的生成。
  • 使FlinkRelOptTable扩展接口InitializerExpressionFactory,因为它是Calcite schema查找的Flink外部表的抽象。
  • 在TableSchema中引入TableColumn结构来描述DDL中声明的列的名称/类型/表达式。TableColumn应保持可序列化,以便在BaseCatalogTable中持久化到目录中。
  • 扩展TableSchema以包含所有TableColumn的定义。

测试计划

对于每个新特性,都可以使用单元测试来测试实现。我们还可以为连接器添加集成测试,以验证它是否可以与现有的source实现兼容。

参考文献
[1] https://docs.microsoft.com/en-us/sql/relational-databases/tables/specify-computed-columns-in-a-table?view=sql-server-2016
[2] https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html
[3] https://oracle-base.com/articles/11g/virtual-columns-11gr1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值