FlinkSQL实现行转列

说明

在hive数仓里,实现一个行转列是很常见的操作,那么如何在 FlinkSQL 中也实现类似的功能呢?以下用以一个样本示例数据来说明。

样本数据

以下数据模拟学生的考试成绩数据:

namelist
andy[{“course”:“flink”,“score”:“99”},{“course”:“spark”,“score”:“88”},{“course”:“hadoop”,“score”:“77”}]

数据格式化:

{
	"name": "andy",
	"list": [{
		"course": "flink",
		"score": 99
	}, {
		"course": "spark",
		"score": 88
	}, {
		"course": "hadoop",
		"score": 77
	}]
}

数据ETL要求

要求最终的行转列数据格式为:

namecoursescore
andyflink99
andyspark88
andyhadoop77

这是一个典型的列转行或者一行转多行的场景,需要将 list 列进行拆分成为多行多列,下面介绍两种实现方式.

  • 方式①、使用 Flink 自带的 unnest 函数(反嵌套)解析
  • 方式②、使用自定义 UDTF 函数解析

FlinkSql建表语句

CREATE TABLE ods_kafka_student_scores (
  `name` string,
  `list` ARRAY<ROW<course STRING,score INT>>
)
WITH (
  'connector' = 'kafka', 											-- 使用 kafka connector
  'topic' = 'ods.kafka_student_scores',								-- kafka topic
  'properties.bootstrap.servers' = 'dn3:9092,dn4:9092,dn5:9092',  	-- broker连接信息
  'properties.group.id' = 'andy_flink_test',						-- 消费kafka的group_id
  'scan.startup.mode' = 'earliest-offset', 							-- 读取数据的位置
  'format' = 'json',  												-- 数据源格式为 json
  'json.fail-on-missing-field' = 'false',							-- 字段丢失任务不失败
  'json.ignore-parse-errors' = 'true'  								-- 解析失败则跳过
);

注意:

  • 这里在定义 list 字段类型的时候需要定义为 ARRAY 类型,因为 unnest 函数需要一个数组类型的参数

1、使用 UNNEST 解析

select 
	name,course,score
from ods_kafka_student_scores 
CROSS JOIN UNNEST(`list`) AS t (course,score);


select 
	name,course,score
from ods_kafka_student_scores, UNNEST(`list`) AS t (course,score);


select 
	name,course,score
from ods_kafka_student_scores 
LEFT JOIN UNNEST(`list`) AS t (course,score) on true;

2、使用自定义 UDTF 解析

UDTF(自定义表值函数),自定义表值函数。

将 0 个、1 个或多个标量值作为输入参数(可以是变长参数)。与自定义的标量函数类似,但与标量函数不同。表值函数可以返回任意数量的行作为输出,而不仅是 1 个值。返回的行可以由 1 个或多个列组成。调用一次函数输出多行或多列数据。

必须继承 TableFunction 基类,并实现一个或者多个名为 eval 的方法。

在使用 UDTF 时,需要带上 LATERAL TABLE两个关键字.

@FunctionHint(output = @DataTypeHint("ROW<course STRING,score INT>"))
public class ParserJsonArrayTest extends TableFunction<Row> {

    private static final Logger LOG = Logger.getLogger(ParserJsonArrayTest.class);

    public void eval(String value) {
        try {
            JSONArray arrays = JSONArray.parseArray(value);
            Iterator<Object> iterator = arrays.iterator();
            while (iterator.hasNext()) {
                JSONObject jsonObject = (JSONObject) iterator.next();
                String course = jsonObject.getString("course");
                Integer score = jsonObject.getInteger("score");
                collect(Row.of(course,score));
            }
        } catch (Exception e) {
            LOG.error("Parser json failed :" + e.getMessage());
        }
    }
}

自定义 UDTF 解析的时候,就不需要把 list 字段定义成 ARRAY 类型了,直接定义成 STRING 类型就可以了,并且这种方式会更加的灵活,比如还需要过滤数据或者更复杂的一些操作时都可以在 UDTF 里面完成.

在FlinkSql里使用UDTF

select name,course,url
from ods_kafka_student_scores 
CROSS JOIN lateral TABLE (ParserJsonArrayTest(`list`)) AS t (course,score);

select name,course,url
from ods_kafka_student_scores, lateral TABLE (ParserJsonArrayTest(`list`)) AS t (course,score);

select name,course,url
from ods_kafka_student_scores 
left join lateral TABLE (ParserJsonArrayTest(`list`)) AS t (course,score) on true;

unnest 和 自定义 UDTF 函数在使用的时候都有 3 种写法,前面两种写法的效果其实是一样的,第三种写法相当于 left join 的用法.
区别在于 CROSS JOIN/INNER JOIN: 对于左侧表的每一行,右侧 UDTF 不输出,则这一行不输出.
LEFT JOIN: 对于左侧表的每一行,右侧 UDTF 不输出,则这一行会输出,右侧 UDTF 字段为 null

程序运行结果:

2> andy,flink,99
2> andy,spark,88
2> andy,hadoop,77

说明总结

在实际使用的时候:

  • 如果 unnest 可以满足需求就直接用 unnest 不需要带来额外的开发;
  • 如果 unnest 函数满足不了需求,那么就自定义 UDTF 去完成.
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flink SQL中,可以使用LATERAL TABLE语句实现列转行的需求。具体步骤如下: 1. 创建表并插入数据 假设我们有一个表,包含两列数据:id和urls,其中urls列是一个包含多个url的数组。我们可以使用以下语句创建并插入数据: ```sql CREATE TABLE source_table ( id INT, urls ARRAY<ROW<content_type STRING, url STRING>> ) WITH ( 'connector' = 'values', 'data-id' = 'source_table', 'changelog-mode' = 'I', 'schema' = 'id INT, urls ARRAY<ROW<content_type STRING, url STRING>>' ); INSERT INTO source_table VALUES (1, ARRAY[ROW('tool', 'http://json.la/'), ROW('tool', 'http://www.baidu.com/'), ROW('tool', 'https://hub.fastgit.org/')]); ``` 2. 使用LATERAL TABLE语句实现列转行 接下来,我们可以使用LATERAL TABLE语句将urls列转换为多行数据。具体语句如下: ```sql SELECT id, url.content_type, url.url FROM source_table, LATERAL TABLE(urls) AS t(url) ``` 在这个语句中,我们使用LATERAL TABLE语句将urls列转换为多行数据,并将其命名为t(url)。然后,我们可以使用SELECT语句选择需要的列,其中url.content_type和url.url分别表示数组中的两个字段。 3. 查看结果 执行以上语句后,我们可以得到以下结果: ``` +----+--------------+------------------------+ | id | content_type | url | +----+--------------+------------------------+ | 1 | tool | http://json.la/ | | 1 | tool | http://www.baidu.com/ | | 1 | tool | https://hub.fastgit.org/| +----+--------------+------------------------+ ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值