Esper学习之八:EPL语法(四)
关于EPL,已经写了三篇了,预估计了一下,除了今天这篇,后面还有5篇左右。大家可别嫌多,官方的文档对EPL的讲解有将近140页,我已经尽量将废话都干掉了,再配合我附上的例子,看我的10篇文章比那140页英文文档肯定舒服多了吧。也请各位原谅我一周一篇的速度,毕竟我还要学习,生活,工作,一个都不能少。
今天讲解的内容包括三块:Order by,Limit,Insert into。大家会SQL的应该很熟悉这三个东西,前两个比较简单,Insert into会有一些差别,篇幅也相对多些。
1.Order by
EPL的Order by和SQL的几乎一模一样,作用都是对输出结果进行排序,但是也有一些需要注意的地方。语法如下:
- order by expression [asc | desc] [, expression [asc | desc]] [, ...]
expreession表示要排序的字段,asc表示升序排列(从小到大),desc表示降序排列(从大到小)。举个例子:
- // 每进入5个事件输出一次,并且先按照name升序排列,再按照age降序排列。
- select * from User output every 5 events order by name, age desc
使用方法很简单,除了和SQL相似的特点外,还有他自己需要注意的几点:
a. 如果不特别说明是升序还是降序,默认情况下按照升序排列。
b. 如果order by的子句中出现了聚合函数,那么该聚合函数必须出现在select的子句中。
c. 出现在select中的expression或者在select中定义的expression,在order by中也有效。
d. 如果order by所在的句子没有join或者没有group by,则排序结果幂等,否则为非幂等。
2. Limit
Limit在EPL中和在SQL中也基本一样,不过SQL中是用具体的数字来表示限制范围,而EPL可以是常量或者变量来表示限制范围。语法如下:
- limit row_count [offset offset_count]
row_count表示输出多少行,可以是一个整型常量,也可以是一个整型变量,以方便运行时修改。
offset_count表示在当前结果集中跳过n行然后再输出,同样也可以是一个整型变量。如果不使用此参数,则表示跳过0行,即从第一行输出。举例如下:
- // 输出结果集的第3行到第10行
- select uri, count(*) from WebEvent group by uri output snapshot every 1 minute order by count(*) desc limit 8 offset 2
除了以上的语法,limit还有一种简化的写法,实际上是参照SQL的标准。
- limit offset_count[, row_count]
两个参数的含义和上面的一样,并且我们将上面的例子改写一下:
- // 输出结果集的第3行到第10行
- select uri, count(*) from WebEvent group by uri output snapshot every 1 minute order by count(*) desc limit 2, 8
如果这个两个参数是负数会怎么样呢?
row_count为负数,则无限制输出,若为0,则不输出。当row_count是变量表示并且变量为null,则无限制输出。
offset _count是不允许负数的,如果是变量表示,并且变量值为null或者负数,则EPL会把他假设为0。
3. Insert into
3.1 简单用法
EPL的Insert into和SQL的有比较大的区别。SQL是往一张表里插入数据,而EPL是把一个事件流的计算结果放入另一个事件流,然后可以对这个事件流进行别的计算。所以Insert into的一个好处就是可以将是事件流的计算结果不断级联,对于那种需要将上一个业务的结果数据放到下一个业务处理的场景再适合不过了。除此之外,Insert into还有合并多个计算结果的作用。到这里相信大家已经对他越来越好奇了,不急,咱们先来看看语法:
- insert [istream | irstream | rstream] into event_stream_name [ (property_name [, property_name] ) ]
event_stream_name定义了事件流的名称,在执行完insert的定义之后,我们可以使用select对这个事件流进行别的计算。
istream | irstream | rstream表示该事件流允许另一个事件的输入/输入和输出/输出数据能够进入(解释好像很绕。。一会儿看例子就能明白了)
property_name表示该事件流里包含的属性名称,多个属性名之间用逗号分割,并且用小括号括起来。
上面的说明可能不是很好理解,咱们先看个例子:
- // 将新进入的Asus事件传递到Computer,且Asus的id,size和Computer的cid,csize对应
- insert into Computer(cid,csize) select id,size from Asus
- // 第二种写法
- insert into Computer select id as cid, size as csize Asus
从例子中可以看到,insert into需要配合select进行使用,以表明前一个事件流有哪些计算结果将进入insert into定义的事件流。并且在select中的字段要和insert里的事件流的属性要对应(这里指的对应是数据类型对应,而且属性数量也必须一样)。如果说insert定义的事件流名称在之前已经定义过(insert into中定义的除外),重名是不允许的。
我个人推荐第二种写法,通过as设置的别名即为insert定义的事件流的属性,这样可以避免属性的个数不一致的错误。
刚才说了istream | irstream | rstream的用法,可能有点表述不清楚,这里看一个完整的例子。
- /**
- *
- * @author luonanqin
- *
- */
- class Asus
- {
- private int id;
- private int size;
- public int getId()
- {
- return id;
- }
- public void setId(int id)
- {
- this.id = id;
- }
- public int getSize()
- {
- return size;
- }
- public void setSize(int size)
- {
- this.size = size;
- }
- public String toString()
- {
- return "id: " + id + ", size: " + size;
- }
- }
- class InsertRstreamListener implements UpdateListener
- {
- public void update(EventBean[] newEvents, EventBean[] oldEvents)
- {
- if (newEvents != null)
- {
- for (int i = 0; i < newEvents.length; i++)
- {
- Object id = newEvents[i].get("cid");
- System.out.println("Insert Asus: cid: " + id);
- }
- }
- if (oldEvents != null)
- {
- for (int i = 0; i < oldEvents.length; i++)
- {
- Object id = oldEvents[i].get("cid");
- System.out.println("Remove Asus: cid: " + id);
- }
- }
- System.out.println();
- }
- }
- public class InsertRstreamTest {
- public static void main(String[] args) throws InterruptedException {
- EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
- EPAdministrator admin = epService.getEPAdministrator();
- String asus = Asus.class.getName();
- String insertEPL = "insert rstream into Computer(cid,csize) select id,size from " + asus + ".win:length(1)";
- String insertSelectEPL = "select cid from Computer.win:length_batch(2)";
- EPStatement state = admin.createEPL(insertEPL);
- EPStatement state1 = admin.createEPL(insertSelectEPL);
- state1.addListener(new InsertRstreamListener());
- EPRuntime runtime = epService.getEPRuntime();
- Asus apple1 = new Asus();
- apple1.setId(1);
- apple1.setSize(1);
- System.out.println("Send Asus: " + apple1);
- runtime.sendEvent(apple1);
- Asus apple2 = new Asus();
- apple2.setId(2);
- apple2.setSize(1);
- System.out.println("Send Asus: " + apple2);
- runtime.sendEvent(apple2);
- Asus apple3 = new Asus();
- apple3.setId(3);
- apple3.setSize(3);
- System.out.println("Send Asus: " + apple3);
- runtime.sendEvent(apple3);
- Asus apple4 = new Asus();
- apple4.setId(4);
- apple4.setSize(4);
- System.out.println("Send Asus: " + apple4);
- runtime.sendEvent(apple4);
- Asus apple5 = new Asus();
- apple5.setId(5);
- apple5.setSize(3);
- System.out.println("Send Asus: " + apple5);
- runtime.sendEvent(apple5);
- Asus apple6 = new Asus();
- apple6.setId(6);
- apple6.setSize(4);
- System.out.println("Send Asus: " + apple6);
- runtime.sendEvent(apple6);
- }
- }
执行结果:
- Send Asus: id: 1, size: 1
- Send Asus: id: 2, size: 1
- Send Asus: id: 3, size: 3
- Insert Asus: cid: 1
- Insert Asus: cid: 2
- Send Asus: id: 4, size: 4
- Send Asus: id: 5, size: 3
- Insert Asus: cid: 3
- Insert Asus: cid: 4
- Send Asus: id: 6, size: 4
这个例子中,insertEPL表示当Asus事件从length为1的view中移除时,把移除的事件放入Computer。insertSelectEPL是对Computer的事件流进行计算,这里只是在每进入两个事件时才输出这两个事件的cid。而rstream在这里的表现,从执行结果中可以看到,在进入id为1 2 3的事件后,insertSelectEPL的监听器被触发,因为id为1和2的事件是在发送了Asus的id为2和3的事件之后被移除了,之后就进入了Computer,并满足了length=2,因此在监听器里看到有id为1和2的事件进入了Computer。
如果不显示指定rstream,则insert into只允许istream的事件流进入Computer。如果指定为irstream,那么进入的和移除的Asus都会进入到Computer。
上面的例子都是指定了insert into里事件流会有什么属性,如果不指定会是什么结果呢?请看例句:
- insert into Computer select * from Asus
很容易想到,这里实际上是把进入引擎的Asus事件都传递到Computer定义的事件流中,并且属性什么的完全和Asus一样,可以说是Asus的一个复制版本,只是名字不一样。也许有人觉得这么做没什么意思,直接计算Asus事件流不就可以了么,实际上在业务处理数据时,这种做法就可以屏蔽掉外部的数据来源,做到业务层上的隔离。
假设Asus中还包含其他的JavaBean,同样也可以将这个Bean的数据传递到另一个事件流中。例句如下:
- // Lenovo中包含了thinkpad这个JavaBean
- insert into Computer select thinkpad.* from Lenovo
3.2 Merge Event Stream
insert into除了接收一个流的事件,同时也支持多个流的合并。通俗一点来说,合并的流数据要一致才可以合并。而且在第一次定义insert的事件流以后,别的事件流想要被合并就必须和之前定义的属性数量和数据类型对应。举例如下:
- // 定义Computer并把Asus的数据输入
- insert into Computer(cid, csize) select aid,asize from Asus
- // 根据之前的Computer定义将Lenovo对应的属性输入
- insert into Computer(cid, csize) select lid,lsize from Lenovo
如果说select了多个事件流,但是你只想输入其中一个,应该像下面这样写:
- insert into Computer select l.* from Asus as a, Lenovo as l
除此之外,EPL还支持调用函数转换事件后再输入insert into:
- // 将Lenovo事件转换后输入Computer
- insert into Computer select Converter.convert(l) from Lenovo as l
注意,使用自定义函数一定要返回javabean,map,或者Object数组,且不能用as来为转换后的结果设置别名。
3.3 Decorated Events
之前所见到的不是将事件流整体输入insert,就是将事件流的部分属性输入insert。实际上可以将事件流整体和事件流属性组成的复杂表达式一起放入insert。示例如下:
- insert into Computer select *, size*price as sp from Asus
- // 第一个*表示Asus,size*price的*表示乘法,两者互不影响
如果说别的事件流想进入此insert,那么事件流属性一定要和第一个*表示的所有属性相同。
3.4 Event Objects Instantiated by insert into
前面的所有例子中,对于insert into的事件结构都是在insert子句中配合select子句进行定义的。如果我们想用已经定义好的事件结构是否可以呢?答案是肯定的。但是如果事件是javabean,并且事先没有注册到引擎,则需要insert子句中写上类的全名。例如:
- insert into test.computer.Computer ...
当然,如果在使用之前有注册过,那么使用注册时的名称也是可以的。
因为事件结构是早就定义好的,所以在写select的时候就必须符合insert事件中的属性了,如果属性名称不一样需要使用as加上别名,一样的可以不用设置别名,且数据类型也要一一对应。例如:
- // Computer中包含cid和csize属性
- insert into test.computer.Computer select aid as cid, asize as csize from Dell
但是这种用法在Computer存在包含了参数的构造方法时就显得没那么必须了。先看一个完整例子,你也许就会明白了。
- /**
- *
- * @author luonanqin
- *
- */
- class Car
- {
- private int size;
- private String name;
- private int price;
- public void setSize(int size)
- {
- this.size = size;
- }
- public void setName(String name)
- {
- this.name = name;
- }
- public void setPrice(int price)
- {
- this.price = price;
- }
- public int getSize()
- {
- return size;
- }
- public String getName()
- {
- return name;
- }
- public int getPrice()
- {
- return price;
- }
- }
- class Auto
- {
- private int autoSize;
- private String autoName;
- public Auto(int s, String n)
- {
- this.autoSize = s;
- this.autoName = n;
- }
- public String toString()
- {
- return "AutoSize: " + autoSize + ", AutoName: " + autoName;
- }
- }
- class Benz
- {
- private int benzSize;
- private String benzName;
- public void setBenzSize(int benzSize)
- {
- this.benzSize = benzSize;
- }
- public void setBenzName(String benzName)
- {
- this.benzName = benzName;
- }
- public String toString()
- {
- return "BenzSize: " + benzSize + ", BenzName: " + benzName;
- }
- }
- class InstantiatePopulateListener implements UpdateListener
- {
- public void update(EventBean[] newEvents, EventBean[] oldEvents)
- {
- if (newEvents != null)
- {
- Object car = newEvents[0].getUnderlying();
- System.out.println(car);
- }
- }
- }
- public class InstantiatePopulateTest
- {
- public static void main(String[] args) throws InterruptedException
- {
- EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
- EPAdministrator admin = epService.getEPAdministrator();
- String car = Car.class.getName();
- String auto = Auto.class.getName();
- String benz = Benz.class.getName();
- String cartToAutoEpl = "insert into " + auto + " select size, name from " + car;
- String autoEpl = "select * from " + auto;
- String cartToBenzEpl = "insert into " + benz + " select size as benzSize, name as benzName from " + car;
- // String benzEpl2 = "insert into " + benz + "(benzSize,benzName) select size, name from " + car;<pre name="code" class="java"> String benzEpl = "select * from " + benz;</pre>admin.createEPL(cartToAutoEpl);EPStatement state1 = admin.createEPL(autoEpl);state1.addListener(new InstantiatePopulateListener());admin.createEPL(cartToBenzEpl);EPStatement state2 = admin.createEPL(benzEpl);state2.addListener(new InstantiatePopulateListener());EPRuntime runtime = epService.getEPRuntime();Car c1 = new Car();c1.setSize(1);c1.setName("car1");c1.setPrice(11);runtime.sendEvent(c1);Car c2 = new Car();c2.setSize(2);c2.setName("car2");c2.setPrice(22);runtime.sendEvent(c2);}}
执行结果:
- AutoSize: 1, AutoName: car1
- BenzSize: 1, BenzName: car1
- AutoSize: 2, AutoName: car2
- BenzSize: 2, BenzName: car2
这里的执行结果很容易理解,关键是carToAutoEpl和carToBenzEpl两个句子。
对于Auto的JavaBean,我们可以发现它包含一个有参数的构造函数且没有属性对应的set方法,在carToAutoEpl中,select的内容并没有和属性名称对应起来。这种写法确实是正确的,正因为Auto中定了含参的构造函数,才使得select可以写的更随意。但是一定要记住,构造函数里的参数顺序一定要和select中的属性的数据类型对应起来,如果这里把name和size互换位置,必定报错!
对于Benz的JavaBean,可以看到每个属性都有对应的set方法,而没有含参的构造函数,所以select中属性的名称需要as来设置别名。当然,像benzEpl2那种写法,同样可以避免select中设置别名。
一句话总结,如果JavaBean中有含参的构造函数,EPL中不需要显示写出属性名称。如果没有构造函数,那么必须包含set方法,且select中要写出具体的属性。这几种写法各有各的好处,大家使用时可针对具体的情况选择性使用。
Esper学习之八:EPL语法(四)的更多相关文章
- Esper学习之十二:EPL语法(八)
今天的内容十分重要,在Esper的应用中是十分常用的功能之一.它是一种事件集合,我们可以对这个集合进行增删查改,所以在复杂的业务场景中我们肯定不会缺少它.它就是Named Window. 由于本篇篇幅 ...
- Esper学习之七:EPL语法(三)
1.Aggregation 和SQL一样,EPL也有Aggregation,即聚合函数.语法如下: aggregate_function([all|distinct] expression) aggr ...
- Esper学习之六:EPL语法(二)
中秋三天,说闲也不闲,调调工作的代码,倒还解决不少问题.不过也是因为最近工作忙的缘故,Esper被我冷落不少日子了,趁着今天最后一天,赶紧写一篇出来. 从上一篇开始说EPL的语法,主要是关于注解的.今 ...
- Esper学习之五:EPL语法(一)
上篇说到了Esper的Context,要是不了解的同学请参看<Esper学习之四:Context>,看过的同学如果还是不理解的话可以给我评论,我将会尽可能的解答.之前有些同学问我Conte ...
- Esper学习之十一:EPL语法(七)
上一篇说到了EPL如何访问关系型数据库这种数据源,实际上别的数据源,比如:webservice.分布式缓存.非关系型数据库等等,Esper提供了统一的数据访问接口.然后今天会讲解如何创建另外一种事件类 ...
- Esper学习之九:EPL语法(五)
本篇的内容主要包括了Subquery(也就是子查询)和Join,内容不少,但是不难,基本上和sql差不太多. 1.Subquery EPL里的Subquery和sql的类似,是否比sql的用法更多我不 ...
- vue第四单元(初识vue-在页面中直接引入vue框架-学习使用vue语法-vue的指令-介绍data用法-methods用法)
第四单元(初识vue-在页面中直接引入vue框架-学习使用vue语法-vue的指令-介绍data用法-methods用法) #课程目标 了解 vue 框架的特点 掌握创建 vue 实例 掌握 data ...
- Esper学习之十五:Pattern(二)
上一篇开始了新一轮语法——Pattern的讲解,一开始为大家普及了几个基础知识,其中有说到操作符.当时只是把它们都列举出来了,所以今天这篇就是专门详解这些操作符的,但是由于篇幅限制,本篇先会讲几个,剩 ...
- Esper学习之四:Context
上周末打球实在太累了,就没来得及更新,只是列了个提纲做做准备,发现Context还是有很多内容的.结果也花了不少时间才写完,所以这篇需要各位慢慢消化,并且最好多写几个例子加深理解. 如果有不了解Esp ...
随机推荐
- 弹出输入框后,将listview内容遮住,解决方案
转自http://blog.csdn.net/silence_cdsn/article/details/7987063 更改listview的布局属性 之前的布局: <ListView andr ...
- css 阻止元素中的文本。双击选中
//firefox -moz-user-select: none; //chrome.safari -webkit-user-select: none; //ie -ms-user-select: n ...
- NHibernate 基础教程
NHibernate之旅系列文章导航 宣传语 NHibernate.NHibernate教程.NHibernate入门.NHibernate下载.NHibernate教程中文版.NHibernate实 ...
- 绝对震撼 10个实用的jQuery/HTML5插件
在HTML5的世界里,我们见证了无数的特效奇迹,但很多特效我们很难在网页中应用,今天我们要分享10款效果震撼但是又比较实用的jQuery/HTML5插件,希望这些项目在应用的过程中也能给你带来设计灵感 ...
- Java读写配置文件——Properties类的简要使用笔记
任何编程语言都有自己的读写配置文件的方法和格式,Java也不例外. 在Java编程语言中读写资源文件最重要的类是Properties,功能大致如下: 1. 读写Properties文件 2. 读写XM ...
- Windows 2008驱动安装失败的原因及解决方法
希望这些内容能够帮助各位朋友顺利地在Windows Server 2008系统环境下安装使用好各种设备的驱动程序! 寻找安装失败原因 一般来说,当我们将目标设备的驱动安装光盘正确放置到Windows ...
- ubuntu14下搭建svn
1.安装 查看是否安装 svn help 安装了卸载 sudo apt-get remove --purge subversion 安装 sudo apt-get update sudo apt-ge ...
- zookeeper入门系列讲解
zookeeper可谓是目前使用最广泛的分布式组件了.其功能和职责单一,但却非常重要. 在现今这个年代,介绍zookeeper的书和文章可谓多如牛毛,本人不才,试图通过自己的理解来介绍zooke ...
- Linux命令缩写的全称
[目录|文件] ls : list(列出目录内容) pwd : print work directory(打印当前目录,现示当前工作目录的绝对路径) cd : change directory(改变目 ...
- 给一个由n-1个整数组成的未排序的序列,其元素都是1~n中的不同的整数。如何在线性时间复杂度内寻找序列中缺失的整数
思路分析:尼玛这不就是等差数列么.首先将该n-1个整数相加,得到sum,然后用(1+n)n/2减去sum,得到的差即为缺失的整数.因为1~n一共n个数,n个数的和为(1+n)n/2,而未排序数列的和为 ...