介绍

今天在Flink 1.7.2版本上跑一个Flink SQL 示例 RetractPvUvSQL,报

Exception in thread "main" org.apache.flink.table.api.ValidationException: SQL validation failed. From line 1, column 19 to line 1, column 51: Cannot apply 'DATE_FORMAT' to arguments of type 'DATE_FORMAT(<VARCHAR(65536)>, <CHAR(2)>)'. Supported form(s): '(TIMESTAMP, FORMAT)'

从提示看应该是不支持参数为字符串,接下来我们自定义一个UDF函数来支持这种场景。

官网不建议使用DATE_FORMAT(timestamp, string) 这种方式

RetractPvUvSQL 代码

public class RetractPvUvSQL {

    public static void main(String[] args) throws Exception {
ParameterTool params = ParameterTool.fromArgs(args);
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = TableEnvironment.getTableEnvironment(env); DataStreamSource<PageVisit> input = env.fromElements(
new PageVisit("2017-09-16 09:00:00", 1001, "/page1"),
new PageVisit("2017-09-16 09:00:00", 1001, "/page2"), new PageVisit("2017-09-16 10:30:00", 1005, "/page1"),
new PageVisit("2017-09-16 10:30:00", 1005, "/page1"),
new PageVisit("2017-09-16 10:30:00", 1005, "/page2")); // register the DataStream as table "visit_table"
tEnv.registerDataStream("visit_table", input, "visitTime, userId, visitPage"); Table table = tEnv.sqlQuery(
"SELECT " +
"visitTime, " +
"DATE_FORMAT(max(visitTime), 'HH') as ts, " +
"count(userId) as pv, " +
"count(distinct userId) as uv " +
"FROM visit_table " +
"GROUP BY visitTime");
DataStream<Tuple2<Boolean, Row>> dataStream = tEnv.toRetractStream(table, Row.class); if (params.has("output")) {
String outPath = params.get("output");
System.out.println("Output path: " + outPath);
dataStream.writeAsCsv(outPath);
} else {
System.out.println("Printing result to stdout. Use --output to specify output path.");
dataStream.print();
}
env.execute();
} /**
* Simple POJO containing a website page visitor.
*/
public static class PageVisit {
public String visitTime;
public long userId;
public String visitPage; // public constructor to make it a Flink POJO
public PageVisit() {
} public PageVisit(String visitTime, long userId, String visitPage) {
this.visitTime = visitTime;
this.userId = userId;
this.visitPage = visitPage;
} @Override
public String toString() {
return "PageVisit " + visitTime + " " + userId + " " + visitPage;
}
}
}

UDF实现

实现参数为字符串的日期解析

public class DateFormat extends ScalarFunction {

    public String eval(Timestamp t, String format) {
return new SimpleDateFormat(format).format(t);
} /**
* 默认日期格式:yyyy-MM-dd HH:mm:ss
*
* @param t
* @param format
* @return
*/
public static String eval(String t, String format) {
try {
Date originDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(t);
return new SimpleDateFormat(format).format(originDate);
} catch (ParseException e) {
throw new RuntimeException("日期:" + t + "解析为格式" + format + "出错");
}
}
}

因为flink 已经内置DATE_FORMAT函数,这里我们改个名字:DATEFORMAT

  //register the function
tEnv.registerFunction("DATEFORMAT", new DateFormat()); Table table = tEnv.sqlQuery(
"SELECT " +
"visitTime, " +
"DATEFORMAT(max(visitTime), 'HH') as ts, " +
"count(userId) as pv, " +
"count(distinct userId) as uv " +
"FROM visit_table " +
"GROUP BY visitTime");

从UDF函数注册源码看,自定义函数在Table API或SQL API 都可以使用

  /**
* Registers a [[ScalarFunction]] under a unique name. Replaces already existing
* user-defined functions under this name.
*/
def registerFunction(name: String, function: ScalarFunction): Unit = {
// check if class could be instantiated
checkForInstantiation(function.getClass) // register in Table API functionCatalog.registerFunction(name, function.getClass) // register in SQL API
functionCatalog.registerSqlFunction(
createScalarSqlFunction(name, name, function, typeFactory)
)
}

执行的结果:

printing result to stdout. Use --output to specify output path.
6> (true,2017-09-16 10:30:00,10,1,1)
4> (true,2017-09-16 09:00:00,09,1,1)
4> (false,2017-09-16 09:00:00,09,1,1)
6> (false,2017-09-16 10:30:00,10,1,1)
4> (true,2017-09-16 09:00:00,09,2,1)
6> (true,2017-09-16 10:30:00,10,2,1)
6> (false,2017-09-16 10:30:00,10,2,1)
6> (true,2017-09-16 10:30:00,10,3,1) Process finished with exit code 0

我们看下这个结果是什么意思:

Flink RetractStream 用true或false来标记数据的插入和撤回,返回true代表数据插入,false代表数据的撤回,在网上看到一个图很直观地说明RetractStream 为什么存在?

看我们的source数据,9点与10点半的数据刚开始pv,uv都为新增,对应的第二条数据来的时候,pv发生变化, 此时要撤掉第一次的结果,更新为新的结果数据 ,就好比我们有时候更新数据的一种办法先删除再插入,后面到来的数据以此类推。

总结

1.Flink处理数据把表转换为流的时候,可以使用toAppendStream与toRetractStream,前者适用于数据追加的场景, 后者适用于更新,删除场景

2.FlinkSQL中可以使用我们自定义的函数.Flink UDF自定义函数实现:evaluation方法必须定义为public,命名为eval。evaluation方法的输入参数类型和返回值类型决定着函数的输入参数类型和返回值类型。evaluation方法也可以被重载实现多个eval。同时evaluation方法支持变参数,例如:eval(String... strs)。

Flink RetractStream示例及UDF函数实现的更多相关文章

  1. hive UDF函数

    —虽然Hive提供了很多函数,但是有些还是难以满足我们的需求.因此Hive提供了自定义函数开发 —自定义函数包括三种UDF.UADF.UDTF —UDF(User-Defined-Function) ...

  2. 【Spark篇】---SparkSql之UDF函数和UDAF函数

    一.前述 SparkSql中自定义函数包括UDF和UDAF UDF:一进一出  UDAF:多进一出 (联想Sum函数) 二.UDF函数 UDF:用户自定义函数,user defined functio ...

  3. Spark注册UDF函数,用于DataFrame DSL or SQL

    import org.apache.spark.sql.SparkSession import org.apache.spark.sql.functions._ object Test2 { def ...

  4. hive 中简单的udf函数编写

    .注册函数,使用using jar方式在hdfs上引用udf库. $hive.注销函数,只需要删除mysql的hive数据记录即可. delete from func_ru ; delete from ...

  5. pyspark 编写 UDF函数

    pyspark 编写 UDF函数 前言 以前用的是Scala,最近有个东西要用Python,就查了一下如何编写pyspark的UDF. pyspark udf 也是先定义一个函数,例如: def ge ...

  6. 如何编写自定义hive UDF函数

    Hive可以允许用户编写自己定义的函数UDF,来在查询中使用.Hive中有3种UDF: UDF:操作单个数据行,产生单个数据行: UDAF:操作多个数据行,产生一个数据行. UDTF:操作一个数据行, ...

  7. 自定义UDF函数应用异常

    自定义UDF函数应用异常 版权声明:本文为yunshuxueyuan原创文章.如需转载请标明出处: http://www.cnblogs.com/sxt-zkys/QQ技术交流群:299142667 ...

  8. Hive扩展功能(三)--使用UDF函数将Hive中的数据插入MySQL中

    软件环境: linux系统: CentOS6.7 Hadoop版本: 2.6.5 zookeeper版本: 3.4.8 主机配置: 一共m1, m2, m3这五部机, 每部主机的用户名都为centos ...

  9. Hive UDF函数构建

    1. 概述 UDF函数其实就是一个简单的函数,执行过程就是在Hive转换成MapReduce程序后,执行java方法,类似于像MapReduce执行过程中加入一个插件,方便扩展.UDF只能实现一进一出 ...

  10. IDEA如何将写好的java类(UDF函数)打成jar包上传linux

    一.编写一个UDF函数,实现将字符串大写转小写 import org.apache.hadoop.hive.ql.exec.UDF; import org.apache.hadoop.io.Text; ...

随机推荐

  1. 精读代码,实战进阶&实践Task2

    背景 从零入门AI生图原理&实践 是 Datawhale 2024 年 AI 夏令营第四期的学习活动("AIGC"方向),基于魔搭社区"可图Kolors-LoRA ...

  2. Camera | 4.瑞芯微平台MIPI摄像头应用程序编写

    前面3篇我们讲解了camera的基础概念,MIPI协议,CSI2,常用命令等,本文带领大家入门,如何用c语言编写应用程序来操作摄像头. Linux下摄像头驱动都是基于v4l2架构,要基于该架构编写摄像 ...

  3. python开发环境安装-包含Anaconda的安装配置和pycharm的安装

    一. 需要得安装包 1.  Anaconda3-5.3.0-Windows-x86_64.exe  python环境 2.pycharm-professional-2021.2.2.exe      ...

  4. 冲刺 NOIP 400pts + 之神仙专题

    冲刺专题之 \(DP\) \(T_A\) Helping People $$codeforces$$ 题意 给定一个长为 \(n\) 序列 \(A\) , 其中有 \(q\) 个区间 \([l , r ...

  5. 虚拟机中创建的centos网络ping不通 - ping unkown host

    一.查看虚拟机设置 二.su 进入超级管理员账户  ls /etc/sysconfig/network-scripts 查看虚拟机网络信息 第一个,每一个机器的配置文件名不同 进入到  network ...

  6. 同步多个mysql 到一个

    了解大概 Ref: is it possible that canal set with multiple mysql database source 使用 canal https://dev.mys ...

  7. Seata 核心源码详解

    参考文章: 分布式事务实战方案汇总 https://www.cnblogs.com/yizhiamumu/p/16625677.html 分布式事务原理及解决方案案例https://www.cnblo ...

  8. mybatis springboot多数据源,根据使用的数据库不同,执行不同的sql语句

    springboot 多数据源配置就不说了,百度太多的用例, 这里只说下在多数据源下切换执行sql逻辑 1.xml sql嵌套,通过<if>标签来判断,用的是mybatis自己sql动态拼 ...

  9. 《Effective TypeScript》条款21 - 类型扩展

    本文主要通过一些实际的代码示例,来帮助大家理解什么是类型扩展,本文主要内容如下: 什么是类型扩展 代码示例 总结 什么是类型扩展? TypeScript 需要从你指定的单一值中决定一组可能的值,这个过 ...

  10. Asp.net core 学习笔记 Image processing (ImageSharp)

    请移步修订版 : ASP.NET Core Library – ImageSharp .net 的生态烂是真的, 很多硬需求都没有人做, 开源的做着做着就闭源了的也很多. 今天说说 image pro ...