介绍

今天在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. 一次生产环境mysql迁移操作(二)mysql空间释放(碎片整理)

    一次生产环境mysql迁移操作(一)数据归档 一次生产环境mysql迁移操作(二)mysql空间释放(碎片整理) 上文中增加了定时归档,现在一些大表磁盘空间一直不释放,导致数据库文件越来越大.现在介绍 ...

  2. Linux input 子系统详解

    1. 模块概述 1.1.相关资料和代码研究 drivers/input/ include/uapi/linux/input-event-codes.h 2. 模块功能 linux核心的输入框架 3. ...

  3. 技术如何通过API接口获取自己想要同款商品的数据

    确定数据源: 首先,你需要确定哪些平台或服务提供商提供了你感兴趣的商品数据.例如,电商平台.品牌商.市场调研公司等. 了解API文档: 访问提供商的开发者门户网站,阅读API文档.文档会详细介绍如何使 ...

  4. 安装nvm,并通过nvm安装nodejs

    转载请注明出处: 1.安装nvm 打开终端,然后运行以下命令来下载并安装nvm: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39 ...

  5. 使用 SSH 转义代码来控制连接

    OpenSSH 最常被忽视的一个非常有用的功能是能够从连接内部控制会话的某些方面.通过使用 SSH 转义代码,我们能够在会话内部与本地 SSH 软件进行交互. 强制从客户端断开连接(如何退出卡住或冻结 ...

  6. 使用 nuxi dev 启动 Nuxt 应用程序的详细指南

    title: 使用 nuxi dev 启动 Nuxt 应用程序的详细指南 date: 2024/9/2 updated: 2024/9/2 author: cmdragon excerpt: 摘要:本 ...

  7. 深入浅出Stream流

    Java 8的新特性之一就是流stream,配合同版本出现的 Lambda ,使得操作集合(Collection)提供了极大的便利. 案例引入 在JAVA中,涉及到对数组.Collection等集合类 ...

  8. C# WinForm避免程序重复启动,限制程序只能运行一个实例【转】

    记录一下,原文:https://blog.csdn.net/xggbest/article/details/104231935 禁止多个进程运行,当重复运行时激活以前的进程 Program.cs: u ...

  9. 【YashanDB知识库】YAS-00220, utf8 sequence is wrong

    [问题分类]功能使用 [关键字]YAS-00220,utf8 sequence is wrong [问题描述]在数据库执行select语句时,或者在exp导出数据库用户时报这个错误,表字段中同时存在n ...

  10. 八,SpringBoot Web 开发访问静态资源(附+详细源码剖析)

    八,SpringBoot Web 开发访问静态资源(附+详细源码剖析) @ 目录 八,SpringBoot Web 开发访问静态资源(附+详细源码剖析) 1. 基本介绍 2. 快速入门 2.1 准备工 ...