Flink RetractStream示例及UDF函数实现
介绍
今天在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函数实现的更多相关文章
- hive UDF函数
虽然Hive提供了很多函数,但是有些还是难以满足我们的需求.因此Hive提供了自定义函数开发 自定义函数包括三种UDF.UADF.UDTF UDF(User-Defined-Function) ...
- 【Spark篇】---SparkSql之UDF函数和UDAF函数
一.前述 SparkSql中自定义函数包括UDF和UDAF UDF:一进一出 UDAF:多进一出 (联想Sum函数) 二.UDF函数 UDF:用户自定义函数,user defined functio ...
- Spark注册UDF函数,用于DataFrame DSL or SQL
import org.apache.spark.sql.SparkSession import org.apache.spark.sql.functions._ object Test2 { def ...
- hive 中简单的udf函数编写
.注册函数,使用using jar方式在hdfs上引用udf库. $hive.注销函数,只需要删除mysql的hive数据记录即可. delete from func_ru ; delete from ...
- pyspark 编写 UDF函数
pyspark 编写 UDF函数 前言 以前用的是Scala,最近有个东西要用Python,就查了一下如何编写pyspark的UDF. pyspark udf 也是先定义一个函数,例如: def ge ...
- 如何编写自定义hive UDF函数
Hive可以允许用户编写自己定义的函数UDF,来在查询中使用.Hive中有3种UDF: UDF:操作单个数据行,产生单个数据行: UDAF:操作多个数据行,产生一个数据行. UDTF:操作一个数据行, ...
- 自定义UDF函数应用异常
自定义UDF函数应用异常 版权声明:本文为yunshuxueyuan原创文章.如需转载请标明出处: http://www.cnblogs.com/sxt-zkys/QQ技术交流群:299142667 ...
- Hive扩展功能(三)--使用UDF函数将Hive中的数据插入MySQL中
软件环境: linux系统: CentOS6.7 Hadoop版本: 2.6.5 zookeeper版本: 3.4.8 主机配置: 一共m1, m2, m3这五部机, 每部主机的用户名都为centos ...
- Hive UDF函数构建
1. 概述 UDF函数其实就是一个简单的函数,执行过程就是在Hive转换成MapReduce程序后,执行java方法,类似于像MapReduce执行过程中加入一个插件,方便扩展.UDF只能实现一进一出 ...
- IDEA如何将写好的java类(UDF函数)打成jar包上传linux
一.编写一个UDF函数,实现将字符串大写转小写 import org.apache.hadoop.hive.ql.exec.UDF; import org.apache.hadoop.io.Text; ...
随机推荐
- 存储过程test按钮式灰的
PL/SQL Developer中,存储过程无法调试的问题解决办法 在Oracle10中新建了一个用户,然后编写存储过程在PL/SQL Developer中调试,提示 ORA-0131: Insuff ...
- Antd-React-TreeSelect前端搜索过滤
在开发过程中,但是antd中的搜索会把多余的也会带出来 就例如下图,我们本想去搜索1但是他会把其子节点都带出来,其实我们的本意是像搜2一样或者当中间隔层处理 但是我们该如何解决这样的问题呢如何做到下面 ...
- 禅道项目管理系统权限绕过漏洞(QVD-2024-15263)
本文所涉及的任何技术.信息或工具,仅供学习和参考之用,请勿将文章内的相关技术用于非法目的,如有相关非法行为与文章作者无关.请遵守<中华人民共和国网络安全法>. 1. 概述 1.1 基本信息 ...
- k8s获取集群内所有在使用镜像
kubectl get pods --all-namespaces -o jsonpath="{..image}" | tr -s '[[:space:]]' ' ' | sort ...
- 并查集noi水题 (P1955 [NOI2015]程序自动分析)
现将输入排序,把merge排在前面 ,避免冗余计算 1 n=rd(); 2 FOR(i,1,n) 3 { 4 s[i].x=rd(),a[++tot]=s[i].x, 5 s[i].y=rd(),a[ ...
- 基于gitee+hexo搭建个人博客
gitee准备 注册好gitee git安装与配置 下载git默认安装,配置 在之前下载的目录下,右键,选择[Git Bash Here] 配置用户 git config --global user. ...
- 中考游记 & 暑假集训大记
中考游记 & 暑假集训大记 前言 如今已经回归 \(OI\) ,望着如烟的往事,或是将将知道的讯息,心中早是凄然. 我真的希望这世间有我所期望的浦岛隧道,带回所有的遗憾,同时带走迷茫与害怕,重 ...
- 2024 NepCTF
NepCTF NepMagic -- CheckIn 直接玩游戏就能出 注意有一关要把隐藏的方块全找到 NepCamera 先使用tshark读取数据 结果文件中发现大量jpeg头ffd8ffe0. ...
- psutil 检测exe是否已经运行
安装依赖 pip install psutil 代码 import psutil def check_if_process_running(process_name): ''' Check if th ...
- python pyqt6 QMainWindow 使用QComboBox 窗口移位
QMainWindow 可拖拽移动,但是点击QComboBox 时,窗口有时会误识别为拖拽移动,导致窗口自动移动位置 在QMainWindow中,新增事件筛选器 # 避免点击QComboBox时,窗 ...