Flink之DataSet 的Join、Cross使用

本文探讨了Flink中的DataSet API如何进行Join和Cross操作。详细解释了内连接(默认)、左连接、右连接及全连接的用法,并举例说明了笛卡尔积的实现,展示了输出结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Join 默认就是内连接,还有leftOuterJoin,rightOuterJoin,fullOuterJoin
cross 笛卡尔积

package com.ruozedata.flink

import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.api.scala._
import scala.collection.mutable.ListBuffer

object FlinkJoinApp {
   
   
  def main(args: Array[String]): Unit = {
   
   

    val env=ExecutionEnvironment.getExecutionEnvironment

    val data1=ListBuffer[(Int,String)]()
    data1.append((1,"ruoze"))
    data1.append((2,"jepson"))
    data1.append((3,"xingxing"))

    val data2=ListBuffer[(Int,String)]()
    data2.append((1,"beijing"))
    data2.append((2,"shanghai"))
    data2.append((4,"hangzhou"))

    val a
<think>我们正在讨论Flink中的CROSSJOINLATERAL的使用。用户要求一个使用示例。根据Flink官方文档,CROSSJOINLATERAL(也称为表函数连接)用于将表与表函数的结果进行连接。表函数是一种用户自定义函数,它可以返回多行结果(而标量函数只返回单个值)。步骤:1.首先需要定义一个表函数(TableFunction),它可以是继承自`TableFunction`类的自定义函数。2.在SQL查询中,使用`CROSSJOINLATERAL`(或`LEFTJOINLATERAL`)来连接主表和表函数的结果。示例场景:假设我们有一个订单表(orders),每个订单有一个包含多个产品的ID列表(用逗号分隔的字符串)。我们还有一个表函数(split)用于将字符串拆分成多行(即每个产品一行)。然后我们想将每个订单拆分成多行,每行对应订单中的一个产品。步骤分解:a.定义表函数:这里我们使用内置的或自定义的表函数。Flink内置了一些函数,但拆分字符串并返回多行的函数可能需要自定义。b.在SQL中使用LATERALJOIN。注意:在FlinkSQL中,表函数连接有两种写法:-使用`CROSSJOINLATERAL`:如果表函数返回空,则不会生成该行的任何结果。-使用`LEFTJOINLATERAL`:即使表函数返回空,也会保留主表的行,并用空值填充表函数的结果列。下面给出一个完整的示例,包括自定义表函数和在SQL中使用。自定义表函数(Scala示例):```scalaimportorg.apache.flink.table.annotation.DataTypeHintimportorg.apache.flink.table.annotation.FunctionHintimportorg.apache.flink.table.functions.TableFunctionimportorg.apache.flink.types.Row//自定义表函数,用于拆分字符串@FunctionHint(output=newDataTypeHint("ROW<productSTRING>"))classSplitFunctionextendsTableFunction[Row]{defeval(str:String,delimiter:String=","):Unit={if(str!=null){str.split(delimiter).foreach{s=>collect(Row.of(s))}}}}```在FlinkTableAPI中注册并使用:```scalaimportorg.apache.flink.table.api._importorg.apache.flink.table.api.bridge.scala._//创建表环境valenv=StreamExecutionEnvironment.getExecutionEnvironmentvaltableEnv=StreamTableEnvironment.create(env)//注册表函数tableEnv.createTemporarySystemFunction("split",newSplitFunction)//假设有一个订单表,包含字段:order_id,products//创建订单表(这里使用虚拟数据)tableEnv.executeSql("""|CREATETABLEorders(|order_idSTRING,|productsSTRING|)WITH(|'connector'='datagen',|'number-of-rows'='3'|)""".stripMargin)//使用CROSSJOINLATERAL拆分每个订单的产品列表valresult=tableEnv.sqlQuery("""|SELECTorder_id,product|FROMorders,|LATERALTABLE(split(products))AST(product)""".stripMargin)//打印结果result.execute().print()```注意:在SQL中,我们使用了`LATERALTABLE(split(products))`,这相当于`CROSSJOINLATERAL`。也可以显式地写成:`FROMordersCROSSJOINLATERALTABLE(split(products))AST(product)`另外,如果使用LEFTJOINLATERAL,则即使拆分结果为空,订单也会被保留:```sqlSELECTorder_id,productFROMordersLEFTJOINLATERALTABLE(split(products))AST(product)ONTRUE```注意:在LEFTJOINLATERAL中,必须加上`ONTRUE`,因为这是一个横向连接,连接条件始终为真(连接条件由表函数的输出行决定,但这里我们只是将表函数应用到每一行)。以上示例展示了如何将每个订单的产品字符串拆分成多行,每行一个产品。相关问题:1.表函数连接与普通连接有什么区别?2.在FlinkSQL中,除了表函数,还有其他类型的函数吗?3.如何处理表函数返回空值的情况?4.在流处理模式下,表函数连接会产生什么样的输出?(例如,当订单表是流时)</think>### Apache Flink CROSS JOIN LATERAL 使用示例 在 Apache Flink 中,`CROSS JOIN LATERAL` 用于将表与表函数(Table Function)的结果进行连接。这种连接方式允许为输入表的每一行调用表函数,并将函数输出的多行结果与原始行连接。以下是详细的使用示例: #### 1. **场景说明** 假设有两个数据源: - **订单表** `Orders(order_id, product_list)`:包含订单ID和产品ID列表(如 `"A,B,C"`) - **产品表** `Products(product_id, price)`:包含产品ID和价格 需要将订单中的产品列表拆分成单个产品,并与产品表关联获取价格。 #### 2. **完整代码示例(Flink SQL)** ```sql -- 注册表函数:拆分字符串为多行 CREATE TEMPORARY FUNCTION SplitProducts AS 'com.example.udf.SplitFunction'; -- 创建订单表 CREATE TABLE Orders ( order_id STRING, product_list STRING ) WITH (...); -- 指定数据源连接器 -- 创建产品表 CREATE TABLE Products ( product_id STRING, price DECIMAL(10, 2) ) WITH (...); -- 指定数据源连接器 -- 使用 CROSS JOIN LATERAL 连接 SELECT o.order_id, p.product_id, pr.price FROM Orders AS o CROSS JOIN LATERAL TABLE(SplitProducts(o.product_list)) AS p(product_id) -- 拆分产品列表 JOIN Products AS pr ON p.product_id = pr.product_id; -- 关联产品价格 ``` #### 3. **表函数实现(Java)** ```java import org.apache.flink.table.functions.TableFunction; import org.apache.flink.types.Row; public class SplitFunction extends TableFunction<Row> { public void eval(String productList) { if (productList != null) { for (String product : productList.split(",")) { collect(Row.of(product.trim())); // 输出拆分后的每个产品 } } } } ``` #### 4. **关键点说明** 1. **表函数作用**: - `SplitProducts(o.product_list)` 将 `"A,B,C"` 拆分为三行:`[A]`、`[B]`、`[C]` - 通过 `CROSS JOIN LATERAL` 将每个订单与拆分结果连接 2. **连接逻辑**: ```plaintext 原始订单: [order_1, "A,B"] ↓ LATERAL 展开 → [order_1, A] → [order_1, B] ↓ 关联产品表 → [order_1, A, 10.0] (假设A的价格是10) → [order_1, B, 15.0] ``` 3. **与普通 JOIN 的区别**: - `CROSS JOIN LATERAL` 依赖左侧表的每一行生成结果 - 表函数可为单行输入生成多行输出(类似 `flatMap`) - 普通 JOIN 无法实现行级扩展功能 #### 5. **流处理注意事项** - 需启用状态保留:`table.exec.state.ttl=3600000`(防止状态无限增长) - 水位线传播:确保输入表的水位线正确传播到结果表 - 处理乱序事件:建议在源表定义 `WATERMARK` #### 典型应用场景 1. 解析嵌套 JSON/数组数据 2. 机器学习特征展开(如将向量拆分为多列) 3. 日志字段拆分(如将 CSV 日志拆分为结构化字段) [^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值