Flink系列之:使用flink查询数据和插入数据

SELECT 语句和 VALUES 语句是使用 TableEnvironment 的 sqlQuery() 方法指定的。该方法以表的形式返回 SELECT 语句(或 VALUES 语句)的结果。 Table 可以在后续的 SQL 和 Table API 查询中使用、转换为 DataStream 或写入 TableSink。 SQL 和 Table API 查询可以无缝混合,并进行整体优化并转换为单个程序。

为了在 SQL 查询中访问表,它必须在 TableEnvironment 中注册。可以通过 TableSource、Table、CREATE TABLE 语句、DataStream 注册表。或者,用户还可以在 TableEnvironment 中注册目录来指定数据源的位置。

为了方便起见,Table.toString() 自动在其 TableEnvironment 中以唯一名称注册该表并返回该名称。因此,Table 对象可以直接内联到 SQL 查询中,如下面的示例所示。

注意:包含不受支持的 SQL 功能的查询会导致 TableException。以下部分列出了批处理表和流表上 SQL 支持的功能。

一、指定查询

以下示例显示如何在注册表和内联表上指定 SQL 查询。

  • Java版本
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

// 从外部源摄取数据流
DataStream<Tuple3<Long, String, Integer>> ds = env.addSource(...);

// 使用内联(未注册)表的 SQL 查询
Table table = tableEnv.fromDataStream(ds, $("user"), $("product"), $("amount"));
Table result = tableEnv.sqlQuery(
  "SELECT SUM(amount) FROM " + table + " WHERE product LIKE '%Rubber%'");

// 使用已注册的表进行 SQL 查询
// 将数据流注册为视图“Orders”
tableEnv.createTemporaryView("Orders", ds, $("user"), $("product"), $("amount"));
// 对表运行 SQL 查询并将结果作为新表检索
Table result2 = tableEnv.sqlQuery(
  "SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");

// 创建并注册 TableSink
final Schema schema = Schema.newBuilder()
    .column("product", DataTypes.STRING())
    .column("amount", DataTypes.INT())
    .build();

final TableDescriptor sinkDescriptor = TableDescriptor.forConnector("filesystem")
    .schema(schema)
    .format(FormatDescriptor.forFormat("csv")
        .option("field-delimiter", ",")
        .build())
    .build();

tableEnv.createTemporaryTable("RubberOrders", sinkDescriptor);

// 在表上运行 INSERT SQL 并将结果发送到 TableSink
tableEnv.executeSql(
  "INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
  • Scala版本
val env = StreamExecutionEnvironment.getExecutionEnvironment
val tableEnv = StreamTableEnvironment.create(env)

// 从外部源摄取数据流
val ds: DataStream[(Long, String, Integer)] = env.addSource(...)

// 使用内联(未注册)表的 SQL 查询
val table = ds.toTable(tableEnv, $"user", $"product", $"amount")
val result = tableEnv.sqlQuery(
  s"SELECT SUM(amount) FROM $table WHERE product LIKE '%Rubber%'")

// 使用已注册的表进行 SQL 查询
// 将数据流注册为视图“Orders”
tableEnv.createTemporaryView("Orders", ds, $"user", $"product", $"amount")
// 对表运行 SQL 查询并将结果作为新表检索
val result2 = tableEnv.sqlQuery(
  "SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")

// 创建并注册 TableSink
val schema = Schema.newBuilder()
  .column("product", DataTypes.STRING())
  .column("amount", DataTypes.INT())
  .build()

val sinkDescriptor = TableDescriptor.forConnector("filesystem")
  .schema(schema)
  .format(FormatDescriptor.forFormat("csv")
    .option("field-delimiter", ",")
    .build())
  .build()

tableEnv.createTemporaryTable("RubberOrders", sinkDescriptor)

// 在表上运行 INSERT SQL 并将结果发送到 TableSink
tableEnv.executeSql(
  "INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
  • python
env = StreamExecutionEnvironment.get_execution_environment()
table_env = StreamTableEnvironment.create(env)

# 使用内联(未注册)表的 SQL 查询
# 元素数据类型:BIGINT、STRING、BIGINT
table = table_env.from_elements(..., ['user', 'product', 'amount'])
result = table_env \
    .sql_query("SELECT SUM(amount) FROM %s WHERE product LIKE '%%Rubber%%'" % table)

// 创建并注册 TableSink
schema = Schema.new_builder()
    .column("product", DataTypes.STRING())
    .column("amount", DataTypes.INT())
    .build()

sink_descriptor = TableDescriptor.for_connector("filesystem")
    .schema(schema)
    .format(FormatDescriptor.for_format("csv")
        .option("field-delimiter", ",")
        .build())
    .build()

t_env.create_temporary_table("RubberOrders", sink_descriptor)

// 在表上运行 INSERT SQL 并将结果发送到 TableSink
table_env \
    .execute_sql("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")

二、执行查询

可以通过TableEnvironment.executeSql()方法执行SELECT语句或VALUES语句将内容收集到本地。该方法将 SELECT 语句(或 VALUES 语句)的结果作为 TableResult 返回。与 SELECT 语句类似,可以使用 Table.execute() 方法执行 Table 对象,以将查询内容收集到本地客户端。 TableResult.collect() 方法返回一个可关闭的行迭代器。除非收集了所有结果数据,否则选择作业将不会完成。我们应该通过 CloseableIterator#close() 方法主动关闭作业以避免资源泄漏。我们还可以通过 TableResult.print() 方法将选择结果打印到客户端控制台。 TableResult 中的结果数据只能访问一次。因此,collect() 和 print() 不能先后调用。

TableResult.collect() 和 TableResult.print() 在不同的检查点设置下的行为略有不同(要为流作业启用检查点,请参阅检查点配置)。

  • 对于没有检查点的批处理作业或流作业,TableResult.collect() 和 TableResult.print() 既没有精确一次也没有至少一次保证。查询结果一旦生成就可以立即被客户端访问,但是当作业失败并重新启动时将会抛出异常。
  • 对于具有一次性检查点的流作业,TableResult.collect() 和 TableResult.print() 保证端到端的一次性记录交付。只有在相应的检查点完成后,客户端才能访问结果。
  • 对于具有至少一次检查点的流作业,TableResult.collect() 和 TableResult.print() 保证端到端至少一次记录传递。查询结果一旦生成就可以立即被客户端访问,但相同的结果可能会被多次传递。

Java:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env, settings);

tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");

// 执行 SELECT 语句
TableResult tableResult1 = tableEnv.executeSql("SELECT * FROM Orders");
// 使用 try-with-resources 语句确保迭代器将自动关闭
try (CloseableIterator<Row> it = tableResult1.collect()) {
    while(it.hasNext()) {
        Row row = it.next();
        // handle row
    }
}

// 执行表
TableResult tableResult2 = tableEnv.sqlQuery("SELECT * FROM Orders").execute();
tableResult2.print();

Scala:

val env = StreamExecutionEnvironment.getExecutionEnvironment()
val tableEnv = StreamTableEnvironment.create(env, settings)
// 启用检查点
tableEnv.getConfig.set(
  ExecutionCheckpointingOptions.CHECKPOINTING_MODE, CheckpointingMode.EXACTLY_ONCE)
tableEnv.getConfig.set(
  ExecutionCheckpointingOptions.CHECKPOINTING_INTERVAL, Duration.ofSeconds(10))

tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")

// 执行 SELECT 语句
val tableResult1 = tableEnv.executeSql("SELECT * FROM Orders")
val it = tableResult1.collect()
try while (it.hasNext) {
  val row = it.next
  // 处理行
}
finally it.close() // close the iterator to avoid resource leak

// 执行表
val tableResult2 = tableEnv.sqlQuery("SELECT * FROM Orders").execute()
tableResult2.print()

Python:

env = StreamExecutionEnvironment.get_execution_environment()
table_env = StreamTableEnvironment.create(env, settings)
# 启用检查点
table_env.get_config().set("execution.checkpointing.mode", "EXACTLY_ONCE")
table_env.get_config().set("execution.checkpointing.interval", "10s")

table_env.execute_sql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")

# 执行 SELECT 语句
table_result1 = table_env.execute_sql("SELECT * FROM Orders")
table_result1.print()

# 执行表
table_result2 = table_env.sql_query("SELECT * FROM Orders").execute()
table_result2.print()

三、语法

Flink 使用 Apache Calcite 解析 SQL,它支持标准 ANSI SQL。

以下 BNF 语法描述了批处理和流式查询中支持的 SQL 功能的超集。操作部分显示了支持的功能的示例,并指出哪些功能仅支持批处理或流查询。

query:
    values
  | WITH withItem [ , withItem ]* query
  | {
        select
      | selectWithoutFrom
      | query UNION [ ALL ] query
      | query EXCEPT query
      | query INTERSECT query
    }
    [ ORDER BY orderItem [, orderItem ]* ]
    [ LIMIT { count | ALL } ]
    [ OFFSET start { ROW | ROWS } ]
    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY]

withItem:
    name
    [ '(' column [, column ]* ')' ]
    AS '(' query ')'

orderItem:
    expression [ ASC | DESC ]

select:
    SELECT [ ALL | DISTINCT ]
    { * | projectItem [, projectItem ]* }
    FROM tableExpression
    [ WHERE booleanExpression ]
    [ GROUP BY { groupItem [, groupItem ]* } ]
    [ HAVING booleanExpression ]
    [ WINDOW windowName AS windowSpec [, windowName AS windowSpec ]* ]

selectWithoutFrom:
    SELECT [ ALL | DISTINCT ]
    { * | projectItem [, projectItem ]* }

projectItem:
    expression [ [ AS ] columnAlias ]
  | tableAlias . *

tableExpression:
    tableReference [, tableReference ]*
  | tableExpression [ NATURAL ] [ LEFT | RIGHT | FULL ] JOIN tableExpression [ joinCondition ]

joinCondition:
    ON booleanExpression
  | USING '(' column [, column ]* ')'

tableReference:
    tablePrimary
    [ matchRecognize ]
    [ [ AS ] alias [ '(' columnAlias [, columnAlias ]* ')' ] ]

tablePrimary:
    [ TABLE ] tablePath [ dynamicTableOptions ] [systemTimePeriod] [[AS] correlationName]
  | LATERAL TABLE '(' functionName '(' expression [, expression ]* ')' ')'
  | [ LATERAL ] '(' query ')'
  | UNNEST '(' expression ')'

tablePath:
    [ [ catalogName . ] databaseName . ] tableName

systemTimePeriod:
    FOR SYSTEM_TIME AS OF dateTimeExpression

dynamicTableOptions:
    /*+ OPTIONS(key=val [, key=val]*) */

key:
    stringLiteral

val:
    stringLiteral

values:
    VALUES expression [, expression ]*

groupItem:
    expression
  | '(' ')'
  | '(' expression [, expression ]* ')'
  | CUBE '(' expression [, expression ]* ')'
  | ROLLUP '(' expression [, expression ]* ')'
  | GROUPING SETS '(' groupItem [, groupItem ]* ')'

windowRef:
    windowName
  | windowSpec

windowSpec:
    [ windowName ]
    '('
    [ ORDER BY orderItem [, orderItem ]* ]
    [ PARTITION BY expression [, expression ]* ]
    [
        RANGE numericOrIntervalExpression {PRECEDING}
      | ROWS numericExpression {PRECEDING}
    ]
    ')'

matchRecognize:
    MATCH_RECOGNIZE '('
    [ PARTITION BY expression [, expression ]* ]
    [ ORDER BY orderItem [, orderItem ]* ]
    [ MEASURES measureColumn [, measureColumn ]* ]
    [ ONE ROW PER MATCH ]
    [ AFTER MATCH
      ( SKIP TO NEXT ROW
      | SKIP PAST LAST ROW
      | SKIP TO FIRST variable
      | SKIP TO LAST variable
      | SKIP TO variable )
    ]
    PATTERN '(' pattern ')'
    [ WITHIN intervalLiteral ]
    DEFINE variable AS condition [, variable AS condition ]*
    ')'

measureColumn:
    expression AS alias

pattern:
    patternTerm [ '|' patternTerm ]*

patternTerm:
    patternFactor [ patternFactor ]*

patternFactor:
    variable [ patternQuantifier ]

patternQuantifier:
    '*'
  | '*?'
  | '+'
  | '+?'
  | '?'
  | '??'
  | '{' { [ minRepeat ], [ maxRepeat ] } '}' ['?']
  | '{' repeat '}'

Flink SQL 对标识符(表、属性、函数名)使用类似于 Java 的词法策略:

  • 无论标识符是否被引用,其大小写都会被保留。
  • 之后,标识符将区分大小写进行匹配。
  • 与 Java 不同,反引号允许标识符包含非字母数字字符(例如 SELECT a AS my field FROM t)。

字符串文字必须用单引号引起来(例如,SELECT ‘Hello World’)。复制单引号以进行转义(例如,SELECT ‘It’s me’)。

Flink SQL> SELECT 'Hello World', 'It''s me';
+-------------+---------+
|      EXPR$0 |  EXPR$1 |
+-------------+---------+
| Hello World | It's me |
+-------------+---------+
1 row in set

字符串文字支持 Unicode 字符。如果需要显式 unicode 代码点,请使用以下语法:

  • 使用反斜杠 () 作为转义字符(默认):SELECT U&‘\263A’
  • 使用自定义转义字符:SELECT U&‘#263A’ UECAPE ‘#’
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Flink 中将多个数据插入 HBase,你可以使用 HBaseSinkFunction。下面是一个简单的示例代码,演示了如何使用 Flink 将多个数据插入 HBase: ```java import org.apache.flink.api.common.functions.MapFunction; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.connectors.hadoop.HadoopOutputFormat; import org.apache.flink.streaming.connectors.hadoop.utils.HadoopUtils; import org.apache.flink.streaming.connectors.hbase.HBaseSinkFunction; import org.apache.flink.streaming.connectors.hbase.HBaseTableSchema; import org.apache.flink.streaming.connectors.hbase.util.HBaseConfigurationUtil; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.util.Bytes; public class FlinkHBaseInsert { public static void main(String[] args) throws Exception { // 设置执行环境 StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // 创建数据源,这里假设数据源是一个 Tuple2<String, Integer> 类型的流 DataStream<Tuple2<String, Integer>> input = env.fromElements( new Tuple2<>("key1", 10), new Tuple2<>("key2", 20), new Tuple2<>("key3", 30) ); // 创建 HBaseTableSchema,定义表名、列簇名和列名 HBaseTableSchema schema = new HBaseTableSchema(); schema.setTableName("my_table"); schema.addColumnFamily("cf1"); schema.addColumn("cf1", "col1"); // 创建 HBaseSinkFunction,用于将数据写入 HBase HBaseSinkFunction<Tuple2<String, Integer>> sinkFunction = new HBaseSinkFunction<>( HBaseConfigurationUtil.getHBaseConfiguration(), schema, (value) -> { Put put = new Put(Bytes.toBytes(value.f0)); // 使用 Tuple2 的第一个字段作为 rowkey put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("col1"), Bytes.toBytes(value.f1.toString())); // 插入数据到 cf1:col1 列 return put; } ); // 将数据流写入 HBase input.map(new MapFunction<Tuple2<String, Integer>, Tuple2<String, Integer>>() { @Override public Tuple2<String, Integer> map(Tuple2<String, Integer> value) throws Exception { return value; } }).addSink(sinkFunction); // 执行任务 env.execute("Flink HBase Insert"); } } ``` 在这个示例中,我们首先创建了一个数据流 `input`,其中包含了多个 Tuple2<String, Integer> 类型的数据。然后,我们创建了一个 `HBaseTableSchema`,定义了要插入的表名、列簇名和列名。接下来,我们创建了一个 `HBaseSinkFunction`,用于将数据写入 HBase。在 `HBaseSinkFunction` 中,我们使用 `Tuple2` 的第一个字段作为 rowkey,将其转换为 `Put` 对象,并设置要插入的列簇和列。 最后,我们通过调用 `addSink` 将数据流写入 HBase。在这个示例中,我们使用了 `map` 函数将输入流中的数据转换为 `Tuple2` 类型,然后将其传递给 `HBaseSinkFunction`。 请注意,你需要根据实际情况修改示例代码中的表名、列簇名、列名和数据类型,以及配置 HBase 的连接信息。确保你已经正确配置了 HBase 的环境和依赖项。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最笨的羊羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值