Flink(十四)Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性

Flink 系列文章

Flink(一)1.12.7或1.13.5详细介绍及本地安装部署、验证
Flink(二)1.13.5二种部署方式(Standalone、Standalone HA )、四种提交任务方式(前两种及session和per-job)验证详细步骤
Flink(三)flink重要概念(api分层、角色、执行流程、执行图和编程模型)及dataset、datastream详细示例入门和提交任务至on yarn运行
Flink(四)介绍Flink的流批一体、transformations的18种算子详细介绍、Flink与Kafka的source、sink介绍
Flink(五)source、transformations、sink的详细示例(一)
Flink(五)source、transformations、sink的详细示例(二)-source和transformation示例
Flink(五)source、transformations、sink的详细示例(三)-sink示例
Flink(六)Flink四大基石之Window详解与详细示例(一)
Flink(六)Flink四大基石之Window详解与详细示例(二)
Flink(七)Flink四大基石之Time和WaterMaker详解与详细示例(watermaker基本使用、kafka作为数据源的watermaker使用示例以及超出最大允许延迟数据的接收实现)
Flink(八)Flink四大基石之State概念、使用场景、持久化、批处理的详解与keyed state和operator state、broadcast state使用和详细示例
Flink(九)Flink四大基石之Checkpoint容错机制详解及示例(checkpoint配置、重启策略、手动恢复checkpoint和savepoint)
Flink(十)source、transformations、sink的详细示例(二)-source和transformation示例【补充示例】
Flink(十一)Flink配置flink-conf.yaml详细说明(HA配置、checkpoint、web、安全、zookeeper、historyserver、workers、zoo.cfg)
Flink(十二)Flink source和sink 的 clickhouse 详细示例
Flink(十三)Flink 的table api与sql的基本概念、通用api介绍
Flink(十四)Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性
Flink(十五)Flink 的table api与sql之流式概念-配置时间属性和如何处理更新结果
Flink(十六)Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式
Flink(十七)Flink 的table api与sql之Table API: Table API 支持的操作
Flink(十八)Flink 的table api与sql之SQL: SQL 支持的操作和语法
Flink(十九)Flink 的table api与sql之内置函数: Table API 和 SQL 中的内置函数
Flink(二十)Flink 的table api与sql之SQL Client: 不用编写代码就可以尝试 Flink SQL,可以直接提交 SQL 任务到集群上
Flink(二十一)Flink 的table api与sql之table api与sql使用示例


文章目录


本文基于flink1.17版本,介绍了flink的数据类型的全部内容,包含数据类型定义、自定义数据类型、类型转换与提取。

本文全部是说明性的,为后续的table api和sql使用奠定基础。

本文分为四个部分,即数据类型介绍、数据类型种类、数据类型转换和数据类型提取。

一、Flink SQL数据类型介绍

1、数据类型

在 Flink 的 Table 生态系统中,数据类型 描述了数据的逻辑类型,可以用来表示转换过程中输入、输出的类型。

Flink 的数据类型类似于 SQL 标准中的术语数据类型,但包含了值的可空性,以便于更好地处理标量表达式。

以下是一些数据类型的例子:

java 复制代码
INT
INT NOT NULL
INTERVAL DAY TO SECOND(3)
ROW<myField ARRAY<BOOLEAN>, myOtherField TIMESTAMP(3)>

可在下文中找到所有预先定义好的数据类型。

2、Table API 中的数据类型

在定义 connector、catalog、用户自定义函数时,使用 JVM 相关 API 的用户可能会使用到 Table API 中基于 org.apache.flink.table.types.DataType 的一些实例。

数据类型 实例有两个职责:

  • 作为逻辑类型的表现形式,定义 JVM 类语言或 Python 语言与 Table 生态系统的边界,而不是以具体的物理表现形式存在于数据的传输过程或存储中。
  • 可选的: 在与其他 API 进行数据交换时,为 Planner 提供这些数据物理层面的相关提示。

对于基于 JVM 的语言,所有预定义的数据类型都可以在 org.apache.flink.table.api.DataTypes 下找到。

使用 Table API 编程时,建议使用星号引入所有相关依赖,以获得更流畅的 API 使用体验:

java 复制代码
import static org.apache.flink.table.api.DataTypes.*;

DataType t = INTERVAL(DAY(), SECOND(3));

在Table 生态系统中,当需要将 SQL 中的数据类型对应到实际编程语言中的数据类型时,就需要有物理提示。物理提示明确了对应过程中应该使用哪种数据格式。

比如,在 source 端产生数据时,可以规定:TIMESTAMP 的逻辑类型,在底层要使用 java.sql.Timestamp 这个类表示,而不是使用默认的 java.time.LocalDateTime 类。有了物理提示,可以帮助 Flink 运行时根据提供的类将数据转换为其内部数据格式。同样在 sink 端,定义好数据格式,以便能从 Flink 运行时获取、转换数据。

下面的例子展示了如何声明一个桥接转换类:

java 复制代码
// 告诉 Flink 运行时使用 java.sql.Timestamp 处理数据,而不是 java.time.LocalDateTime
DataType t = DataTypes.TIMESTAMP(3).bridgedTo(java.sql.Timestamp.class);

// 告诉 Flink 运行时使用基本的 int 数组来处理数据,而不是用包装类 Integer 数组
DataType t = DataTypes.ARRAY(DataTypes.INT().notNull()).bridgedTo(int[].class);

只有在扩展 API 时才需要使用到物理提示。使用预定义的 source、sink 以及 Flink 函数时,不需要用到物理提示。在使用 Table API 编写程序时,Flink 会忽略物理提示(例如 field.cast(TIMESTAMP(3).bridgedTo(Timestamp.class)))

二、Flink SQL 数据类型种类

本部分介绍所有Flink支持的预定义的数据类型,可能和Flink的版本有关,本部分介绍的是Flink 1.17版本。

所有预定义的数据类型都可以通过org.apache.flink.table.api.DataTypes来查看。

1、Character Strings

1)、CHAR

固定长度的字符串

  • sql 类型
sql 复制代码
CHAR
CHAR(n)
  • java类型
java 复制代码
DataTypes.CHAR(n)

n的长度没有指定的时候,默认为1,n的范围为【1, 2,147,483,647】。

2)、VARCHAR / STRING

可变长度的字符串

  • sql 类型
sql 复制代码
VARCHAR
VARCHAR(n)

STRING
  • java类型
java 复制代码
DataTypes.VARCHAR(n)

DataTypes.STRING()

n不指定的时候,默认为1。n的范围为【1, 2,147,483,647】。

STRING就是VARCHAR(2147483647)。

2、Binary Strings

1)、BINARY

固定长度的二进制字符串,即字节的序列。n不指定的时候,默认为1。n的范围为【1, 2,147,483,647】。

  • sql类型
sql 复制代码
BINARY
BINARY(n)
  • java类型
java 复制代码
DataTypes.BINARY(n)

2)、VARBINARY / BYTES

可变长度的二进制字符串。n不指定的时候,默认为1。n的范围为【1, 2,147,483,647】。

BYTES就是VARBINARY(2147483647)。

  • sql类型
sql 复制代码
VARBINARY
VARBINARY(n)

BYTES
  • java类型
java 复制代码
DataTypes.VARBINARY(n)

DataTypes.BYTES()

3、Exact Numerics

1)、DECIMAL

具有固定精度和小数位数的十进制数的数据类型。

DECIMAL(p, s),p是精度,即数字的位数,s是小数位位数,p范围是【1,38】,s的范围值【0,p】,不指定的时候默认p为10,s为0

NUMERIC(p, s) 和 DEC(p, s) 是一样的。

  • sql类型
sql 复制代码
DECIMAL
DECIMAL(p)
DECIMAL(p, s)

DEC
DEC(p)
DEC(p, s)

NUMERIC
NUMERIC(p)
NUMERIC(p, s)
  • java类型
java 复制代码
DataTypes.DECIMAL(p, s)

2)、TINYINT

是1字节有符号的整型数值,范围是【-128,127】

  • sql类型
sql 复制代码
TINYINT
  • java类型
java 复制代码
DataTypes.TINYINT()

3)、SMALLINT

2字节有符号的整型数字,范围是【-32,768,32,767】。

  • sql类型
sql 复制代码
SMALLINT
  • java类型
java 复制代码
DataTypes.SMALLINT()

4)、INT

4字节的有符号整型数字,范围是【-2,147,483,648 , 2,147,483,647】。

INT和INTEGER一样的。

  • sql类型
sql 复制代码
INT

INTEGER
  • java类型
java 复制代码
DataTypes.INT()

5)、BIGINT

8字节的有符号整型数字,范围是【 -9,223,372,036,854,775,808 , 9,223,372,036,854,775,807】。

  • sql类型
sql 复制代码
BIGINT
  • java类型
java 复制代码
DataTypes.BIGINT()

4、Approximate Numerics

1)、FLOAT

4 字节单精度浮点数的数据类型。

与 SQL 标准相比,该类型不带参数。

  • sql类型
sql 复制代码
FLOAT
  • java类型
java 复制代码
DataTypes.FLOAT()

2)、DOUBLE

8 字节双精度浮点数的数据类型。

DOUBLE和DOUBLE PRECISION一样。

  • sql类型
sql 复制代码
DOUBLE

DOUBLE PRECISION
  • java类型
java 复制代码
DataTypes.DOUBLE()

5、Date and Time

1)、DATE

由年-月-日组成的日期的数据类型,其值范围从 0000-01-01 到 9999-12-31。

与 SQL 标准相比,范围从 0000 年开始。

  • sql类型
sql 复制代码
DATE
  • java类型
java 复制代码
DataTypes.DATE()

2)、TIME

不带时区的时间的数据类型,由小时:分钟:秒[.分数]组成,精度高达纳秒,值范围为 00:00:00.0000000000 到 23:59:59.9999999999。

与 SQL 标准相比,不支持闰秒(23:59:60 和 23:59:61)。未提供时区时间。

可以使用 TIME(p) 声明类型,其中 p 是秒的小数部分(精度)的位数。p 的值必须介于 0 和 9 之间(包括 0 和 9)。如果未指定精度,则 p 等于 0。

  • sql类型
sql 复制代码
TIME
TIME(p)
  • java类型
java 复制代码
DataTypes.TIME(p)

3)、TIMESTAMP

不带时区的时间戳的数据类型,由年-月-日小时:分钟:秒[.小数]组成,精度高达纳秒,值范围从 0000-01-01 00:00:00.000000000 到 9999-12-31 23:59:59.9999999999。

与SQL标准相比,不支持闰秒(23:59:60和23:59:61),语义更接近java.time.LocalDateTime。

不支持与 BIGINT(一种 JVM 长类型)之间的转换。但是,此类型不受时区限制。有关更多java.time.Instant类语义,请使用TIMESTAMP_LTZ。

可以使用 TIMESTAMP(p) 声明类型,其中 p 是秒的小数部分(精度)的位数。p 的值必须介于 0 和 9 之间(包括 0 和 9)。如果未指定精度,则 p 等于 6。

没有时区的TIMESTAMP§是这种类型的同义词。

  • sql类型
sql 复制代码
TIMESTAMP
TIMESTAMP(p)

TIMESTAMP WITHOUT TIME ZONE
TIMESTAMP(p) WITHOUT TIME ZONE
  • java类型
java 复制代码
DataTypes.TIMESTAMP(p)

4)、TIMESTAMP WITH TIME ZONE

时间戳的数据类型,其时区由年-月-日小时:分钟:秒[.fractional] 区域组成,精度高达纳秒,值范围从 0000-01-01 00:00:00.000000000 +14:59 到 9999-12-31 23:59:59.9999999999 -14:59。

与TIMESTAMP_LTZ相比,时区偏移信息以物理方式存储在每个基准面中。它单独用于每次计算、可视化或与外部系统的通信。

  • sql类型
sql 复制代码
TIMESTAMP WITH TIME ZONE
TIMESTAMP(p) WITH TIME ZONE
  • java类型
java 复制代码
DataTypes.TIMESTAMP_WITH_TIME_ZONE(p)

5)、TIMESTAMP_LTZ

具有本地时区的时间戳的数据类型,由年-月-日小时:分钟:秒[.fractional] 区域组成,精度高达纳秒,值范围从 0000-01-01 00:00:00.000000000 +14:59 到 9999-12-31 23:59:59.9999999999 -14:59

This type fills the gap between time zone free and time zone mandatory timestamp types by allowing the interpretation of UTC timestamps according to the configured session time zone.

可以使用 TIMESTAMP_LTZ(p) 声明类型,其中 p 是秒的小数部分(精度)的位数。p 的值必须介于 0 和 9 之间(包括 0 和 9)。如果未指定精度,则 p 等于 6。

TIMESTAMP§ WITH LOCAL TIME ZONE 是该类型的同义词。

  • sql类型
sql 复制代码
TIMESTAMP_LTZ
TIMESTAMP_LTZ(p)

TIMESTAMP WITH LOCAL TIME ZONE
TIMESTAMP(p) WITH LOCAL TIME ZONE
  • java类型
java 复制代码
DataTypes.TIMESTAMP_LTZ(p)
DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE(p)

6)、INTERVAL YEAR TO MONTH

一组年月间隔类型的数据类型。

这种类型必须按照下面中之一进行参数化:

  • interval of years,
  • interval of years to months,
  • or interval of months.

年-月的间隔由 +年-月组成,值范围从 -9999-11 到 +9999-11。

对于所有类型的resolutions,值表示形式都是相同的。例如,50 的月份间隔始终以年到月的间隔格式表示(具有默认的年份精度):+04-02。

可以使用上述组合声明类型,其中 p 是年数(年份精度)。p 的值必须介于 1 和 4 之间(包括 1 和 4)。如果未指定年份精度,则 p 等于 2。

  • sql类型
sql 复制代码
INTERVAL YEAR
INTERVAL YEAR(p)
INTERVAL YEAR(p) TO MONTH
INTERVAL MONTH
  • java类型
java 复制代码
DataTypes.INTERVAL(DataTypes.YEAR())
DataTypes.INTERVAL(DataTypes.YEAR(p))
DataTypes.INTERVAL(DataTypes.YEAR(p), DataTypes.MONTH())
DataTypes.INTERVAL(DataTypes.MONTH())

7)、INTERVAL DAY TO SECOND

一组day-time间隔类型的数据类型。

这种类型必须按照下面中之一进行参数化:

  • interval of days,
  • interval of days to hours,
  • interval of days to minutes,
  • interval of days to seconds,
  • interval of hours,
  • interval of hours to minutes,
  • interval of hours to seconds,
  • interval of minutes,
  • interval of minutes to seconds,
  • or interval of seconds.

day-time间隔由 +天小时:月:秒.小数组成,值范围为 -999999 23:59:59.999999999 到 +999999 23:59:59.9999999999。对于所有类型的resolutions,值表示形式都是相同的。例如,秒间隔 70 始终以天到秒的间隔格式表示(具有默认精度):+00 00:01:10.000000。

  • sql类型
sql 复制代码
INTERVAL DAY
INTERVAL DAY(p1)
INTERVAL DAY(p1) TO HOUR
INTERVAL DAY(p1) TO MINUTE
INTERVAL DAY(p1) TO SECOND(p2)
INTERVAL HOUR
INTERVAL HOUR TO MINUTE
INTERVAL HOUR TO SECOND(p2)
INTERVAL MINUTE
INTERVAL MINUTE TO SECOND(p2)
INTERVAL SECOND
INTERVAL SECOND(p2)
  • java类型
java 复制代码
DataTypes.INTERVAL(DataTypes.DAY())
DataTypes.INTERVAL(DataTypes.DAY(p1))
DataTypes.INTERVAL(DataTypes.DAY(p1), DataTypes.HOUR())
DataTypes.INTERVAL(DataTypes.DAY(p1), DataTypes.MINUTE())
DataTypes.INTERVAL(DataTypes.DAY(p1), DataTypes.SECOND(p2))
DataTypes.INTERVAL(DataTypes.HOUR())
DataTypes.INTERVAL(DataTypes.HOUR(), DataTypes.MINUTE())
DataTypes.INTERVAL(DataTypes.HOUR(), DataTypes.SECOND(p2))
DataTypes.INTERVAL(DataTypes.MINUTE())
DataTypes.INTERVAL(DataTypes.MINUTE(), DataTypes.SECOND(p2))
DataTypes.INTERVAL(DataTypes.SECOND())
DataTypes.INTERVAL(DataTypes.SECOND(p2))

可以使用上述组合声明类型,其中 p1 是天数的位数(日精度),p2 是秒的小数位数(小数精度)。P1 的值必须介于 1 和 6 之间(包括 1 和 6)。P2 的值必须介于 0 和 9 之间(包括 0 和 9)。如果未指定 p1,则默认情况下等于 2。如果未指定 p2,则默认情况下等于 6。

6、Constructured Data Types

1)、ARRAY

具有相同子类型的元素数组的数据类型。

与 SQL 标准相比,数组的最大基数无法指定,但固定为 2,147,483,647。此外,支持任何有效类型作为子类型。

ARRAY,其中 t 是所包含元素的数据类型。

t ARRAY 是更接近 SQL 标准的同义词。例如,INT ARRAY 等效于 ARRAY

  • sql类型
sql 复制代码
ARRAY<t>
t ARRAY
  • java类型
java 复制代码
DataTypes.ARRAY(t)

2)、MAP

将key(包括 NULL)map到value(包括 NULL)的关联数组的数据类型。map不能包含重复的键;每个键最多可以映射到一个值。

元素类型没有限制;须确保唯一性。

map类型是 SQL 标准的扩展。

  • sql类型
sql 复制代码
MAP<kt, vt>
  • java类型
java 复制代码
DataTypes.MAP(kt, vt)

MAP<kt, vt>,其中 kt 是键元素的数据类型,vt 是值元素的数据类型。

3)、MULTISET

multiset(=bag)的数据类型 ,与set不同,它允许每个元素具有公共子类型的多个实例。每个唯一值(包括 NULL)都映射到某个多重性。

元素类型没有限制;须确保唯一性。

  • sql类型
sql 复制代码
MULTISET<t>
t MULTISET
  • java类型
java 复制代码
DataTypes.MULTISET(t)

MULTISET,其中 t 是所包含元素的数据类型。

t MULTISET 是更接近 SQL 标准的同义词。例如,INT MULTISET 等效于 MULTISET。

4)、ROW

字段序列( a sequence of fields)的数据类型。

字段由字段名称、字段类型和可选描述组成。表中行的最具体类型是行类型。在这种情况下,行的每一列对应于与列具有相同序号位置的行类型的字段。

与 SQL 标准相比,可选字段描述简化了复杂结构的处理。

ROW类似于其他不符合标准的框架中已知的 STRUCT 类型。

  • sql类型
sql 复制代码
ROW<n0 t0, n1 t1, ...>
ROW<n0 t0 'd0', n1 t1 'd1', ...>

ROW(n0 t0, n1 t1, ...>
ROW(n0 t0 'd0', n1 t1 'd1', ...)
  • java类型
java 复制代码
DataTypes.ROW(DataTypes.FIELD(n0, t0), DataTypes.FIELD(n1, t1), ...)
DataTypes.ROW(DataTypes.FIELD(n0, t0, d0), DataTypes.FIELD(n1, t1, d1), ...)

可以使用 ROW<n0 t0 'd0'、n1 t1 'd1'、...> 其中 n 是字段的唯一名称,t 是字段的逻辑类型,d 是字段的描述。

ROW(...) 是更接近 SQL 标准的同义词。例如,ROW(myField INT, myOtherField BOOLEAN) 等价于 ROW<myField INT, myOtherField BOOLEAN>。

7、User-Defined Data Types

尚不完全支持用户定义的数据类型。它们目前(从 Flink 1.11 开始)仅在函数的参数和返回类型中作为未注册的结构化类型公开。

结构化类型类似于面向对象编程语言中的对象。它包含零个、一个或多个属性。每个属性都由名称和类型组成。

有两种结构化类型,如下:

  • 存储在目录中并由目录标识符标识的类型(如 cat.db.MyType)。这些等于结构化类型的 SQL 标准定义。
  • 由实现类(如com.myorg.model.MyType)标识的匿名定义的未注册类型(通常以反射方式提取)。这些在以编程方式定义表程序时很有用。它们允许重用现有的 JVM 类,而无需再次手动定义数据类型的模式。

1)、Registered Structured Types

目前(1.17版本)不支持注册的结构化类型。因此,它们不能存储在目录中或在创建表 DDL 中引用。

2)、Unregistered Structured Types

未注册的结构化类型可以使用自动反射提取从常规 POJO(Plain Old Java Objects 普通旧 Java 对象)创建。

结构化类型的实现类必须满足以下要求:

  • 该类必须是全局可访问的,这意味着它必须声明为公共的、静态的而不是抽象的。
  • 该类必须提供具有零参数的默认构造函数或分配所有字段的完整构造函数。
  • 类的所有字段必须可由公共声明或遵循常见编码样式(如getField(),isField(),field())的getter读取。
  • 类的所有字段都必须可由公共声明、完全赋值构造函数或遵循常见编码样式(如 setField(...)、field(...))的 setter 写入。
  • 所有字段都必须通过反射提取隐式映射到数据类型,或者使用 @DataTypeHint 批注显式映射到数据类型。
  • 声明为静态或暂时性的字段将被忽略。

反射提取支持字段的任意嵌套,只要字段类型不(传递地)引用自身。

声明的字段类(例如,public int age;)必须包含在为本文档中每种数据类型定义的支持的 JVM 桥接类列表中(例如.java.lang.Integer 或 int 表示 INT)。

对于某些类,需要注释才能将类映射到数据类型(例如@DataTypeHint("DECIMAL(10,2)")为java.math.BigDecimal分配固定的精度和小数位数)。

示例如下:

java 复制代码
class User {

    // extract fields automatically
    public int age;
    public String name;

    // enrich the extraction with precision information
    public @DataTypeHint("DECIMAL(10, 2)") BigDecimal totalBalance;

    // enrich the extraction with forcing using RAW types
    public @DataTypeHint("RAW") Class<?> modelClass;
}

DataTypes.of(User.class);

8、Other Data Types

1)BOOLEAN

Data type of a boolean with a (possibly) three-valued logic of TRUE, FALSE, and UNKNOWN.

布尔值的数据类型,其(可能)三值逻辑为 TRUE、FALSE 和 UNKNOWN。

  • sql 类型
sql 复制代码
BOOLEAN
  • java类型
java 复制代码
DataTypes.BOOLEAN()

2)、RAW

Data type of an arbitrary serialized type. This type is a black box within the table ecosystem and is only deserialized at the edges.

任意序列化(arbitrary serialized)类型的数据类型。此类型是表生态系统中的黑盒,仅在边缘反序列化。

原始类型是 SQL 标准的扩展。

  • sql类型
sql 复制代码
RAW('class', 'snapshot')
  • java类型
java 复制代码
DataTypes.RAW(class, serializer)

DataTypes.RAW(class)

可以使用 RAW('class', 'snapshot') 声明该类型,其中 class 是原始类,snapshot是 Base64 编码的序列化 TypeSerializerSnapshot。通常,类型字符串不是直接声明的,而是在保留类型时生成的。

在 API 中,可以通过直接提供类 + 类型序列化程序或传递类并让框架从那里提取类 + 类型序列化程序来声明 RAW 类型。

3)、NULL

用于表示非类型化 NULL 值的数据类型。

NULL类型是 SQL 标准的扩展。null 类型除了 NULL 之外没有其他值,因此,它可以转换为类似于 JVM 语义的任何可空类型。

此类型有助于在使用 NULL 文本的 API 调用中表示未知类型,以及桥接到定义此类类型的格式,例如 JSON 或 Avro。

这种类型在实践中不是很有用,这里只是为了完整性而提到。

  • sql类型
java 复制代码
NULL
  • java类型
java 复制代码
DataTypes.NULL()

三、类型转换

1、CAST 方法

Flink Table API 和 Flink SQL 支持从 输入 数据类型 到 目标 数据类型的转换。有的转换 无论输入值是什么都能保证转换成功,而有些转换则会在运行时失败(即不可能转换为 目标 数据类型对应的值)。 例如,将 INT 数据类型的值转换为 STRING 数据类型一定能转换成功,但无法保证将 STRING 数据类型转换为 INT 数据类型。

在生成执行计划时,Flink 的 SQL 检查器会拒绝提交那些不可能直接转换为 目标 数据类型的SQL,并抛出 ValidationException 异常, 例如从 TIMESTAMP 类型转化到 INTERVAL 类型。 然而有些查询即使通过了 SQL 检查器的验证,依旧可能会在运行期间转换失败,这就需要用户正确处理这些失败了。

在 Flink Table API 和 Flink SQL 中,可以用下面两个内置方法来进行转换操作:

  • CAST:定义在 SQL 标准的 CAST 方法。在某些容易发生转换失败的查询场景中,当实际输入数据不合法时,作业便会运行失败。类型推导会保留输入类型的可空性。
  • TRY_CAST:常规 CAST 方法的扩展,当转换失败时返回 NULL。该方法的返回值允许为空。
java 复制代码
CAST('42' AS INT) --- 结果返回数字 42 的 INT 格式(非空)
CAST(NULL AS VARCHAR) --- 结果返回 VARCHAR 类型的空值
CAST('non-number' AS INT) --- 抛出异常,并停止作业

TRY_CAST('42' AS INT) --- 结果返回数字 42 的 INT 格式
TRY_CAST(NULL AS VARCHAR) --- 结果返回 VARCHAR 类型的空值
TRY_CAST('non-number' AS INT) --- 结果返回 INT 类型的空值
COALESCE(TRY_CAST('non-number' AS INT), 0) --- 结果返回数字 0 的 INT 格式(非空)

下表展示了各个类型的转换程度,"Y" 表示支持,"!" 表示转换可能会失败,"N" 表示不支持:

备注:

所有转化到具有固长或变长的类型时会根据类型的定义来裁剪或填充数据。

使用 TO_TIMESTAMP 方法和 TO_TIMESTAMP_LTZ 方法的场景,不要使用 CAST 或 TRY_CAST。

支持转换,当且仅当用其内部数据结构也支持转化时。转换可能会失败,当且仅当用其内部数据结构也可能会转换失败。

支持转换,当且仅当用使用 RAW 的类和类的序列化器一样。

支持转换,当且仅当用使用 INTERVAL 做"月"到"年"的转换。

支持转换,当且仅当用使用 INTERVAL 做"天"到"时间"的转换。

请注意:无论是 CAST 还是 TRY_CAST,当输入为 NULL ,输出也为 NULL。

2、旧版本 CAST 方法

用户可以通过将参数 table.exec.legacy-cast-behaviour 设置为 enabled 来启用 1.15 版本之前的 CAST 行为。 在 Flink 1.15 版本此参数默认为 disabled。

如果设置为 enabled,请注意以下问题:

  • 转换为 CHAR/VARCHAR/BINARY/VARBINARY 数据类型时,不再自动修剪(trim)或填充(pad)。
  • 使用 CAST 时不再会因为转化失败而停止作业,只会返回 NULL,但不会像 TRY_CAST 那样推断正确的类型。
  • CHAR/VARCHAR/STRING 的转换结果会有一些细微的差别。

不建议 配置此参数,而是 强烈建议 在新项目中保持这个参数为默认禁用,以使用最新版本的 CAST 方法。 在下一个版本,这个参数会被移除。

四、数据类型提取

在 API 中的很多地方,Flink 都尝试利用反射机制从类信息中自动提取数据类型,以避免重复地手动定义 schema。但是,通过反射提取数据类型并不总是有效的,因为有可能会缺失逻辑信息。因此,可能需要在类或字段声明的附近添加额外信息以支持提取逻辑。

下表列出了无需更多信息即可隐式映射到数据类型的类。

如果你打算在 Scala 中实现类,建议使用包装类型(例如 java.lang.Integer)而不是 Scala 的基本类型。如下表所示,Scala 的基本类型(例如 Int 或 Double)会被编译为 JVM 基本类型(例如 int/double)并产生 NOT NULL 语义。此外,在泛型中使用的 Scala 基本类型(例如 java.util.Map[Int, Double])在编译期间会被擦除,导致类信息类似于 java.util.Map[java.lang.Object, java.lang.Object]。

本文档中提到的其他 JVM 桥接类需要 @DataTypeHint 注释。

数据类型 hints 可以参数化或替换单个函数参数和返回类型、结构化类或结构化类的字段的默认提取逻辑。实现者可以通过声明 @DataTypeHint 注解来选择默认提取逻辑的修改程度。

@DataTypeHint 注解提供了一组可选的 hint 参数。其中一些参数如以下示例所示。更多信息可以在注解类的文档中找到。

示例如下:

java 复制代码
import org.apache.flink.table.annotation.DataTypeHint;

class User {

    // 使用默认转换类 `java.lang.Integer` 定义 INT 数据类型
    public @DataTypeHint("INT") Object o;

    // 使用显式转换类定义毫秒精度的 TIMESTAMP 数据类型
    public @DataTypeHint(value = "TIMESTAMP(3)", bridgedTo = java.sql.Timestamp.class) Object o;

    // 通过强制使用 RAW 类型来丰富提取
    public @DataTypeHint("RAW") Class<?> modelClass;

    // 定义所有出现的 java.math.BigDecimal(包含嵌套字段)都将被提取为 DECIMAL(12, 2)
    public @DataTypeHint(defaultDecimalPrecision = 12, defaultDecimalScale = 2) AccountStatement stmt;

    // 定义当类型不能映射到数据类型时,总是将其视为 RAW 类型,而不是抛出异常
    public @DataTypeHint(allowRawGlobally = HintFlag.TRUE) ComplexModel model;
}

以上,基于flink1.17版本,介绍了flink的数据类型的全部内容,包含数据类型定义、自定义数据类型、类型转换与提取。

相关推荐
时差9533 小时前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database
Mephisto.java3 小时前
【大数据学习 | kafka高级部分】kafka的优化参数整理
大数据·sql·oracle·kafka·json·database
山海青风4 小时前
第七篇: BigQuery中的复杂SQL查询
sql·googlecloud
lzhlizihang6 小时前
【Hive sql 面试题】求出各类型专利top 10申请人,以及对应的专利申请数(难)
大数据·hive·sql·面试题
威哥爱编程7 小时前
SQL Server 数据太多如何优化
数据库·sql·sqlserver
Mephisto.java8 小时前
【大数据学习 | kafka高级部分】kafka的kraft集群
大数据·sql·oracle·kafka·json·hbase
Mephisto.java8 小时前
【大数据学习 | kafka高级部分】kafka的文件存储原理
大数据·sql·oracle·kafka·json
W Y8 小时前
【架构-37】Spark和Flink
架构·flink·spark
ycsdn108 小时前
Caused by: org.apache.flink.api.common.io.ParseException: Row too short:
大数据·flink
数新网络12 小时前
《深入浅出Apache Spark》系列②:Spark SQL原理精髓全解析
大数据·sql·spark