在 flink sql 中,对表名、字段名、函数名等是严格区分大小写的,为了兼容 hive 等其他仓库,建议建表时,表名和字段名都采用下划线连接单词的方式,以避免大小写问题。
比如 hive ,是不区分大小写的,所有大写字母最终都会被系统转化为小写字母,此时使用 flink sql 去读写 hive ,出现大写字母时,会出现找不到表或字段的错误。
关键字是不区分大小写的,比如 insert、select、create等。flink sql 中所有的字符串常量都需要使用英文括起来,不要使用英文双引号以及中文符号。
 
https://blog.csdn.net/begefefsef/article/details/126790094

CREATE TABLE

语法概述:
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [catalog_name.][db_name.]table_name
(
{ <physical_column_definition> | <metadata_column_definition> | <computed_column_definition> }[ , ...n]
[ <watermark_definition> ]
[ <table_constraint> ][ , ...n]
)

Columns(字段)

Physical / Regular Columns(物理/常规列)

物理列是数据库中已知的常规列。它们定义物理数据中字段的名称、类型和顺序。因此,物理列表示从外部系统读取和写入的有效负载。
连接器和格式转化使用这些列(按照定义的顺序)来配置自己。其他类型的列可以在物理列之间声明,但不会影响最终的物理模式。
下面的语句创建了一个只有常规列的表:
CREATE TABLE MyTable (
    `user_id` BIGINT,
    `name` STRING
) WITH (
...
);

Metadata Columns(元数据列)

元数据列是SQL标准的扩展,允许访问连接器和/或表中每一行的特定字段。元数据列由metadata关键字表示。例如,元数据列可以用来读取和写入Kafka记录的时间戳,以进行基于时间的操作。
连接器和格式文档列出了每个组件的可用元数据字段。在表的模式中声明元数据列是可选的。
下面的语句创建了一个表,其中包含引用元数据timestamp的附加元数据列:
CREATE TABLE MyTable (
    `user_id` BIGINT,
    `name` STRING,
    `record_time` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' -- 读取和写入kafka记录的时间戳
) WITH (
    'connector' = 'kafka'
...
);
每个元数据字段都由基于字符串的键标识,并具有文档化的数据类型。例如,Kafka连接器暴露了一个元数据字段,该字段由键timestamp和数据类型TIMESTAMP_LTZ(3)标识,可以用于读写记录。
在上面的例子中,元数据列record_time成为表模式的一部分,可以像普通列一样进行转换和存储:
INSERT INTO MyTable SELECT user_id, name, record_time + INTERVAL '1' SECOND FROM MyTable;
为了方便起见,如果将列名直接用于标识元数据,则可以省略FROM子句:
CREATE TABLE MyTable (
    `user_id` BIGINT,
    `name` STRING,
    `timestamp` TIMESTAMP_LTZ(3) METADATA -- 使用列名作为元数据键
) WITH (
    'connector' = 'kafka'
...
);
为方便起见,如果列的数据类型与元数据字段的数据类型不同,可以显式指示强制类型转换,不过要求这两种数据类型是兼容的。
CREATE TABLE MyTable (
    `user_id` BIGINT,
    `name` STRING,
    `timestamp` BIGINT METADATA -- 转化timestamp类型为BIGINT
) WITH (
'connector' = 'kafka'
...
);
默认情况下,planner计划器会假定元数据列可以同时用于读写。然而在许多情况下,外部系统提供的元数据字段用于只读比可写更多。因此,可以使用VIRTUAL关键字将元数据列排除在持久化之外。
CREATE TABLE MyTable (
    `timestamp` BIGINT METADATA, -- query-to-sink schema的一部分
    `offset` BIGINT METADATA VIRTUAL, -- 不是query-to-sink schema的一部分
    `user_id` BIGINT,
    `name` STRING,
) WITH (
    'connector' = 'kafka'
...
);
在上面的示例中,偏移量是一个只读元数据列,并从query-to-sink schema中排除。因此,source-to-query模式(用于SELECT)和query-to-sink(用于INSERT INTO)模式不同:
source-to-query schema:
MyTable(`timestamp` BIGINT, `offset` BIGINT, `user_id` BIGINT, `name` STRING)
query-to-sink schema:
MyTable(`timestamp` BIGINT, `user_id` BIGINT, `name` STRING)

Computed Columns(计算列)

计算列是使用语法column_name AS computed_column_expression生成的虚拟列。
计算列可以引用同一表中声明的其他列的表达式,可以访问物理列和元数据列。列本身并不物理地存储在表中,列的数据类型通过给定的表达式自动派生,不需要手动声明。
计划器会将计算列转换为常规投影。对于优化或水印策略下推,计算列的实际计算可能会跨算子进行,并执行多次,或者在给定查询不需要的情况下跳过。例如,计算列可以定义为:
CREATE TABLE MyTable (
    `user_id` BIGINT,
    `price` DOUBLE,
    `quantity` DOUBLE,
    `cost` AS price * quanitity, -- 执行表达式并接收查询结果
) WITH (
    'connector' = 'kafka'
    ...
);
表达式可以是列、常量或函数的任意组合。表达式不能包含子查询。计算列通常在Flink中用于在CREATE TABLE语句中定义时间属性。可以通过proc AS PROCTIME()使用系统的PROCTIME()函数轻松定义处理时间属性。事件时间属性timestamp可以在水印声明之前进行预处理。例如,如果原始字段不是TIMESTAMP(3)类型或嵌套在JSON字符串中,则可以使用计算列。

SELECT 与 WHERE 子句

SELECT

select 语句的一般语法为:
SELECT select_list FROM table_expression [ WHERE boolean_expression ];
table_expression 可以引用任何数据源。它可以是一个现有表、视图或VALUES子句、多个现有表的连接结果或一个子查询。假设该表在catalog中可用,下面的语句将从Orders中读取所有行。
SELECT * FROM Orders;
select_list中的*号表示查询将会解析所有列。但是,在生产中不鼓励使用。相反,select_list可以指定手动可用列,或者使用可用列进行计算。Orders表有名为order_id、price和tax的列,则可以编写以下查询:
SELECT order_id, price + tax FROM Orders
可以根据WHERE子句筛选数据:
SELECT price + tax FROM Orders WHERE id = 10;

SELECT DISTINCT

如果指定了SELECT DISTINCT,则会从结果集中删除所有重复的行(每组重复的行保留一行):
SELECT DISTINCT id FROM Orders;
对于流式查询,计算查询结果所需的状态可能会无限增长。状态大小取决于不同的数据行数量。可以提供具有适当状态生存时间(TTL)的查询配置,以防止状态存储过大。

WITH子句

WITH提供了一种编写辅助语句的方法,以便在更大的查询中使用。这些语句通常称为公共表表达式(Common Table Expression, CTE),可以认为它们定义了仅用于一个查询的临时视图。
WITH orders_with_total AS (
    SELECT order_id, price + tax AS total
    FROM Orders
)
SELECT order_id, SUM(total) FROM orders_with_total GROUP BY order_id;

PRIMARY KEY

主键约束是Flink用于优化的一个提示。它告诉flink,指定的表或视图的一列或一组列是唯一的,它们不包含null。主列中的任何一列都不能为空。主键唯一地标识表中的一行。
主键约束可以与列定义(列约束)一起声明,也可以作为单行声明(表约束)。只能使用这两种方式之一,如果同时定义多个主键约束,则会引发异常。
SQL标准指定约束可以是强制的,也可以是不强制的。这将控制是否对传入/传出数据执行约束检查。Flink不保存数据,因此我们希望支持的唯一模式是not forced模式。确保查询执行的主键唯一性由用户负责。
注意:在CREATE TABLE语句中,主键约束会改变列的可空性,也就是说,一个有主键约束的列是不能为NULL的。

PARTITIONED BY

根据指定的列对已创建的表进行分区。如果将该表用作filesystem sink,则为每个分区创建一个目录。

WITH选项

用于创建表source/sink的表属性,属性通常用于查找和创建底层连接器。
表达式key1=val1的键和值都应该是字符串字面值。有关不同连接器的所有受支持的表属性,请参阅连接器中的详细信息。
表名可以是三种格式:
  • catalog_name.db_name.table_name
  • db_name.table_name
  • table_name
对于catalog_name.db_name.Table_name,表将被注册到catalog名为“catalog_name”,数据库名为“db_name;
对于db_name.Table_name,表将注册到当前表执行环境的catalog和数据库名为“db_name”;
对于table_name,表将注册到表执行环境的当前catalog和数据库中。
注意:用CREATE table语句注册的表既可以用作表source,也可以用作表sink,我们不能决定它是用作源还是用作接收器,直到它在dml语句中被引用。

WATERMARK

WATERMARK子句用于定义表的事件时间属性,其形式为WATERMARK FOR rowtime_column_name AS watermark_strategy_expression
rowtime_column_name定义一个列,该列被标记为表的事件时间属性。该列必须为TIMESTAMP(3)类型,并且是模式中的顶级列。它可以是一个计算列。
watermark_strategy_expression定义了水印生成策略。它允许任意非查询表达式(包括计算列)来计算水印。表达式返回类型必须为TIMESTAMP(3),表示从Epoch开始的时间戳。返回的水印只有在非空且其值大于先前发出的本地水印时才会发出(以保持升序水印的规定)。框架会对每条记录执行水印生成表达式。框架将周期性地发出生成的最大水印。
如果当前水印与前一个相同,或为空,或返回的水印值小于上次发出的水印值,则不会发出新的水印。水印通过pipeline.auto-watermark-interval配置的时间间隔发出。
如果水印间隔为0ms,生成的水印不为空且大于上次发出的水印,则每条记录都发出一次水印。当使用事件时间语义时,表必须包含事件时间属性和水印策略。
Flink提供了几种常用的水印策略:
  • 严格递增时间戳:WATERMARK FOR rowtime_column AS rowtime_column。发出到目前为止观察到的最大时间戳的水印。时间戳大于最大时间戳的行不属于延迟。
  • 升序时间戳:WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL ‘0.001’ SECOND。发出到目前为止观察到的最大时间戳减去1的水印。时间戳大于或等于最大时间戳的行不属于延迟。
  • 有界乱序时间戳:WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL ‘string’ timeUnit
发出到目前为止观察到的最大时间戳减去指定延迟的水印,例如:WATERMARK FOR
rowtime_column AS rowtime_column - INTERVAL ‘5’ SECOND是一个延迟5秒的水印策略。
CREATE TABLE Orders (
  `user` BIGINT,
  product STRING,
  order_time TIMESTAMP(3),
  WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH ( . . . );

INSERT

通过select查询Insert数据

select 查询结果可以通过使用 insert 子句插入到表中。
INSERT { INTO | OVERWRITE } [catalog_name.][db_name.]table_name [PARTITION part_spec] [column_list] select_statement
part_spec:
    (part_col_name1=val1 [, part_col_name2=val2, ...])
column_list:
    (col_name1 [, column_name2, ...])

OVERWRITE

INSERT OVERWRITE 将覆盖表或分区中的任何现有数据。否则(INTO),将追加新的数据。

PARTITION

PARTITION子句指定插入语句的静态分区列。
-- 创建一个分区表
CREATE TABLE country_page_view (user STRING, cnt INT, date STRING, country STRING)
PARTITIONED BY (date, country)
WITH (...)

-- 向静态分区(date='2019-8-30', country='China')追加数据行
INSERT INTO country_page_view PARTITION (date='2019-8-30', country='China')
SELECT user, cnt FROM page_view_source;

Insert values into tables

可以使用INSERT…VALUES语句将数据直接从SQL插入到表中。
CREATE TABLE students (name STRING, age INT, gpa DECIMAL(3, 2)) WITH (...);
INSERT INTO students
VALUES ('fred flintstone', 35, 1.28), ('barney rubble', 32, 2.32);

ORDER BY子句

在流批任务中均可使用。ORDER BY子句会根据指定的表达式对结果行进行排序。如果根据最左边的表达式比较,两行相等,则继续根据下一个表达式对它们进行比较,以此类推。如果根据所有指定的表达式比较,它们都是相等的,则以依赖于实现的顺序返回它们。
当以流模式运行时,表的主要排序顺序必须根据时间属性进行升序进行排序。所有后续排序都可以自由选择。但是在批处理模式中没有这种限制。
SELECT *
FROM Orders
ORDER BY order_time, order_id;

LIMIT子句

只能在批处理任务中使用。 LIMIT子句限制SELECT语句返回的行数。LIMIT通常与ORDER BY一起使用,以确保结果的确定性。
下面的示例返回Orders表中的前3行。
SELECT *
FROM Orders
ORDER BY orderTime
LIMIT 3;

ALTER TABLE

重命名表
ALTER TABLE [catalog_name.][db_name.]table_name RENAME TO new_table_name
将给定的表名重命名为另一个新表名。 设置或更改表属性:
ALTER TABLE [catalog_name.][db_name.]table_name SET (key1=val1, key2=val2, ...)
给指定的表设置一个或多个属性。如果表中已经设置了特定的属性,则用新值覆盖旧值。

EXPLAIN

EXPLAIN语句用于解释SELECT或INSERT语句的逻辑和优化的查询计划。
 EXPLAIN [([ExplainDetail[, ExplainDetail]*]) | PLAN FOR] <query_statement_or_insert_statement>

案例

Flink SQL> EXPLAIN ESTIMATED_COST, CHANGELOG_MODE, JSON_EXECUTION_PLAN SELECT `count`, word FROM MyTable1
> WHERE word LIKE ''F%''
> UNION ALL
> SELECT `count`, word FROM MyTable2;

EXPLAIN PLAN

== Abstract Syntax Tree ==
LogicalUnion(all=[true])
:- LogicalProject(count=[$0], word=[$1])
:  +- LogicalFilter(condition=[LIKE($1, _UTF-16LE'F%')])
:     +- LogicalTableScan(table=[[default_catalog, default_database, MyTable1]])
+- LogicalProject(count=[$0], word=[$1])
   +- LogicalTableScan(table=[[default_catalog, default_database, MyTable2]])

== Optimized Physical Plan ==
Union(all=[true], union=[count, word])
:- Calc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')])
:  +- TableSourceScan(table=[[default_catalog, default_database, MyTable1]], fields=[count, word])
+- TableSourceScan(table=[[default_catalog, default_database, MyTable2]], fields=[count, word])

== Optimized Execution Plan ==
Union(all=[true], union=[count, word])
:- Calc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')])
:  +- TableSourceScan(table=[[default_catalog, default_database, MyTable1]], fields=[count, word])
+- TableSourceScan(table=[[default_catalog, default_database, MyTable2]], fields=[count, word])
Explain细节:从flink-1.14.x开始支持。打印语句包含指定explain细节的 plan 信息。
  • ESTIMATED_COST:估计成本,生成优化器估计的物理节点的成本信息。
比如:TableSourceScan(…, cumulative cost ={1.0E8 rows, 1.0E8 cpu, 2.4E9 io, 0.0 network, 0.0 memory})。
  • CHANGELOG_MODE:为每个物理节点生成变更日志模式,比如:GroupAggregate(…, changelogMode=[I,UA,D])。
  • JSON_EXECUTION_PLAN:生成json格式的程序执行计划。

DROP

DROP语句用于从当前或指定的Catalog中删除已注册的表/视图/函数。
DROP TABLE
DROP [TEMPORARY] TABLE [IF EXISTS] [catalog_name.][db_name.]table_name
删除指定表名的表。如果要删除的表不存在,则抛出异常。
posted on 2023-09-05 14:43  架构艺术  阅读(488)  评论(0编辑  收藏  举报