Esper学习之九:EPL语法(五)
本篇的内容主要包括了Subquery(也就是子查询)和Join,内容不少,但是不难,基本上和sql差不太多。
1.Subquery
EPL里的Subquery和sql的类似,是否比sql的用法更多我不得而知,毕竟本人是sql菜鸟,只在where语句里用过子查询。废话不多说,先上几个Subquer的简单用法:
子查询结果作为外部事件的属性
- select assetId, (select zone from ZoneClosed.std:lastevent()) as lastClosed from RFIDEvent
上面的例子是说返回当前RFIDEvent的assetId属性值和最新ZoneClosed事件的zone属性值,且以lastClosed作为zone的别名。
子查询关联外部事件的属性
- select * from RfidEvent as RFID where 'Dock 1' = (select name from Zones.std:unique(zoneId) where zoneId = RFID.zoneId)
子查询语句中的where条件可以应用RFID的属性,即内部的zoneId=RFID.zoneId
- select zoneId, (select name from Zones.std:unique(zoneId) where zoneId = RFID.zoneId) as name from RFIDEvent
关联外部事件属性的同时也可以作为外部事件的属性返回。
子查询内部事件作为外部事件的属性
- select (select * from MarketData.std:lastevent()) as md from SuperMarket
每进入一个SuperMarket事件就返回最新的MarketData事件作为属性返回,别名为md
子查询中应用聚合函数
- select * from MarketData where price > (select max(price) from MarketData(symbol='GOOG').std:lastevent())
子查询出得最大price和当前事件的price进行比较
- select * from OrderEvent oe where qty > (select sum(qty) from OrderEvent.win:time(1 hour) pd where pd.client = oe.client)
子查询得出qty的总和和当前事件的qty进行比较
Filter中使用子查询
- select * from BarData(ticker='MSFT', closePrice < (select movAgv from SMA20Stream(ticker='MSFT').std:lastevent()))
子查询返回的movAgv和外部事件的属性closePrice进行比较作为外部事件的一个filter
Pattern中使用子查询
- select * from pattern [
- a=A -> b=B(bvalue = (select d_val from DNamedWindow as d where d.d_id = b.b_id and d.d_id = a.a_id))
- ]
pattern的含义可先不深究,这里只要知道子查询可以用在pattern中就行了。
Expression中使用子查询(什么是Expression?请看《Esper学习之五:EPL语法(一)》的第八点)
- expression subq {
- (select max(quantity) as maxq, min(quantity) as minq from OrderEvent.win:time(1 min))
- }
- select (quantity - minq) / (subq().maxq - subq().minq) as prorated from OrderEvent
以上就是子查询的几种简单用法,不过有几点注意事项是要各位悉知的:
1.子查询的返回必须使用data window或者view来进行限制,控制子查询使用的事件数(data window和view相当于具有某种功能性的事件集合)
2.子查询语句只能由select子句,from子句以及where子句组成,其他的均不支持,比如group by,limit等
3.没有关联外部事件的子查询语句也可以使用聚合函数
4.子查询语句中的select子句必须对所有属性使用聚合函数
5.在使用子查询时,如果子查询的事件和外部事件类型一样,则事件到来时,先经过子查询语句的处理,然后再经过外部语句的处理。如果包含了多个子查询语句,则事件的处理顺序规则较为复杂,本人暂时没做研究。
针对第4点可能说得有些不明白,特此举例说明下:
- class Apple {
- private int price;
- private int size;
- public void setPrice(int price) {
- this.price = price;
- }
- public void setSize(int size) {
- this.size = size;
- }
- public int getPrice() {
- return price;
- }
- public int getSize() {
- return size;
- }
- }
- class Fruit {
- }
- public class Test {
- public static void main(String[] args) throws InterruptedException {
- EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
- EPAdministrator admin = epService.getEPAdministrator();
- String epl1 = "select (select sum(price), sum(size) from " + Apple.class.getName() + ".std:lastevent()) from " + Fruit.class.getName();
- /*
- * 当然,对不同的属性使用不同的聚合函数也是可以的
- */
- // String epl1 = "select (select sum(price), avg(size) from " + Apple.class.getName() + ".std:lastevent()) from " + Fruit.class.getName();
- /*
- * 注意:size没有使用聚合函数,会导致创建epl失败。文档中注明了“The properties of the subselect stream must all be within aggregation functions”.
- * 即子查询中的select子句使用聚合函数时,所查询的属性都要使用聚合函数
- */
- // String epl1 = "select (select sum(price), size from " + Apple.class.getName() + ".std:lastevent()) from " + Fruit.class.getName();
- admin.createEPL(epl1);
- System.out.println("Create epl successfully!");
- }
- }
除了上面的语法外,有几个关键字也是需要大家注意的。
exists
这个exists和exist可不一样,虽说也是用在where子句中(不仅仅是where子句),但是exists前面是没有什么待比较属性的。语法如下:
- exists (subquery)
如果exists后面的subquery有查询结果,则返回true,如果没有查询结果就返回false。所以用在where子句中的示例如下:
- select * from Fruit as F where exists (select * from Apple.std:lastevent() where acount = F.fcount)
既然exists语法返回的时布尔值,那么可不可以用工在Filter中呢?经本人测试,确实可以用。示例如下:
- select * from Fruit(exists (select * from Apple.win:keepall()))
不过在使用exists有两个注意点:
1.exists后面的子查询语句一定要用圆括号括起来,遗漏的话会报语法错误。
2.exists后面的子查询语句只能返回单列值。什么叫单列值?比如:Apple有price和size属性,那么select price和select size都是返回的单列(即一列),select price, size就是多列(即两列)。如果subquery中返回的大于一列,则会报multi-column错误。谨记!
in / not in
用法和sql的一样,具体语法就不说了,直接看例子吧:
- // 当前进入的Apple事件的aPrice存在于过去十秒内进入的Fruit的fPrice即可返回
- select * from Apple where aPrice in (select fPrice from Fruit.win:time(10 s))
注意:从语法上说in/not in前面是expression,并没有规定只能是属性。比如:Apple有aPrice和aSize两个int属性,则where子句可以写成"aPrice+aSize in ....."(这里只是举例,不expression不表示任何意思)
any / some / all
除了用in或者not in判断属性值是否存在于子查询结果中,还可以使用any/some/all并配合一些比较符号与子查询结果进行比较。语法如下:
- expression operator any/some/all (subquery)
- // operator包含:>=, !=, <>, <, <=, >, >=
一个简单的例子:
- // 如果当前Apple事件的aPrice小于前十个Fruit中的任何一个fPrice,则返回此事件
- select * from Apple where aPrice < any (select fPrice from Fruit.win:length(10))
- // 如果当前Apple事件的aPrice小于前十个Fruit中的所有fPrice,则返回此事件
- select * from Apple where aPrice < all (select fPrice from Fruit.win:length(10))
some和any同义,所以用法也相同。既然一样为什么又要弄这么个关键字,我也没搞懂。。。
子查询返回多列数据
子查询可以返回单列数据,也可以返回多列数据。用法很简单,只需要“."(点)就能找到每列的数据了。示例如下:
- select *,
- (select bid, offer from MarketData.std:unique(symbol) as md where md.symbol = oe.symbol) as bidoffer
- from OrderEvent oe
如上所示,子查询语句中包含bid和offer两列,并且这个语句的结果用bidoffer作为别名,所以要想得到bid和offer的具体数据只需要bidoffer.bid和bidoffer.offer即可,简单吧。子查询返回多行数据和返回多列数据类似,只不过数据会以数组形式是返回。
2.Join
Join在sql里是很常见的查询方法,EPL也同样支持,并且包含了full outer join / left outer join / right outer join / inner join等。和sql基本无差别。
inner join
在没有任何关键字的修饰下,即为默认join方式,也就是inner join。必须等到所有join的事件都到了才可能输出,因为要是有where关联两个事件,得满足where条件了才能输出。例如:
- select * from Orange.std:lastevent(), Banana.std:lastevent()
如果只有Orange到或者Banana到都不会输出内容。std:lastevent是一种data window。如果不加特殊修饰的话(特殊修饰?下面会告诉你),事件必须有data window或者view修饰,否则会出现语法错误。当然,不同的事件可以用不同的data window修饰,并没有规定要一样。
评论里c77_cn有问到:
为什么"select a.id, b.field from Event as a, method : Static Class . getMethod (a.id) as b"没有window或者view也报错?
后来我重读了一下官方文档,没有指明必须要window或者view。我这么写是因为我测时候的时候,错误里说明了需要window或者view(在没有unidirectional修饰的情况下,这个修饰就是刚才说的“特殊修饰”)。这个句子正确我猜测method方法是每到一个事件都会被调用,所以能够完成join的工作。如果不用method,在没有window或者view的情况下,两类不同的事件分先后进入引擎,那先进入的的事件不能暂存的话如何完成join呢?
full outer join
上面看到的默认join方式是要求所有join的事件都必须到达引擎才会输出,并且join的事件之间通过where子句设置了条件判断,还得到达的两个事件满足条件了才能输出,而full outer join正好解决了这个问题,不管哪个事件到达,不管是否符合条件,都会输出。例如:
- select * from Orange.std:lastevent() as o full outer join Banana.std:lastevent() as b on o.price = b.price
输出结果有4种可能:
a.当只有Orange事件到达,没有满足join条件,会输出Orange事件,且Banana事件为null。
b.当只有Banana事件到达,没有满足join条件,会输出Banana事件,且Orange事件为null。
c.当两个事件都到达了,且没有满足join条件,即price不相等,则a,b情况各出现一次。
d.当两个事件都到达了,且满足join条件,即price相等,即可输出满足条件的事件。
所以说不管什么情况下,当前进入的事件都会输出,至于join的那个事件,满足即输出事件,不满足即输出null。
left outer join
full outer join输出了所进入的所有事件,不满足join条件的就输出null,而left outer join则规定关键字左边的事件可以即刻输出,而关键字右边的事件必须满足join条件才可输出。示例如下:
- select * from Pink.std:lastevent() as pi left outer join Pear.std:lastevent() as pe on pi.price = pe.price
因为Pink事件在left outer join的左边,所以他的输出不受join条件的限制,即事件到来该怎么输出怎么输出。但是Pear就不同,由于有join条件限制,即两个事件的price要相等,所以如果Pear事件到达的时候,如果没有满足条件的Pink事件,则Pear事件是不会输出的。(注意:输出null也算输出,这里是null都不会输出,即不触发listener)
right outer join
和left outer join相反,在关键字右边的事件不受join条件约束,而左边的事件必须满足join条件才可输出。具体例子我就不举了,大家可以写两个句子试试。
此外,在使用以上4种join的时候,可以多种join混用。比如:
- select * from Apple.std:lastevent() as a
- left outer join Banana.std:lastevent() as b on a.price = b.price
- full outer join Orange.std:lastevent() as o on o.price = a.price
on后面的表达式是join的限制条件,且只能用“=”,如果想用其他操作符,则必须放到where子句中,这点尤其要注意。多个限制条件只能用and连接,不能用逗号,且限制的事件也要一样。比如:
- // a,b分别是两个事件的别名
- // 正确写法
- ……on a.price = b.price and a.size = b.size……
- // 错误写法1:不能用逗号连接
- ……on a.price = b.price, a.size = b.size……
- // 错误写法2:必须针对同样的事件进行限制(c是另一个事件的别名)
- ……on a.price = b.price and a.size = c.size……
Unidirectional Join
之前说到,如果不加特殊修饰,则join的事件都需要data window或者view修饰,目的是为了暂存事件以便等待满足条件的事件并执行join。如果想让某个事件到来时直接触发join,不需要暂存,也就是不需要data window或者view修饰,则需要加上一个特殊关键字——unidirectional。先看一个简单的例子:
- select * from Apple as a unidirectional, Banana.std:lastevent() as b where a.price = b.price
上句的意思是:维持最新的Banana事件,直到一个和Banana的price相等的Apple事件到来时输出两者。
由于有unidirectional的修饰,表明Apple事件是即时出发join操作,也就是说进入此EPL的Apple事件是无状态的。所以当Apple事件到来时,如果没有price相等的Banana,则什么输出也没有,即使下一个Banana事件的price和之前来的Apple的price相等也不会有输出,因为那个Apple事件已经从这个句子的上下文中移除了。为了更好的看到效果,我给大家写了一给完整的例子:
- class Orange {
- private int price;
- public void setPrice(int price) {
- this.price = price;
- }
- public int getPrice() {
- return price;
- }
- @Override
- public String toString() {
- return "Orange price=" + price;
- }
- }
- class Banana {
- private int price;
- public int getPrice() {
- return price;
- }
- public void setPrice(int price) {
- this.price = price;
- }
- @Override
- public String toString() {
- return "Banana price=" + price;
- }
- }
- class JoinUnidirectionalListener implements UpdateListener {
- public void update(EventBean[] newEvents, EventBean[] oldEvents) {
- if (newEvents != null) {
- System.out.println(newEvents[0].get("o") + ", " + newEvents[0].get("b"));
- }
- }
- }
- public class JoinUnidirectionalTest {
- public static void main(String[] args) throws InterruptedException {
- EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
- EPAdministrator admin = epService.getEPAdministrator();
- String epl1 = "select * from " + Orange.class.getName() + " as o unidirectional, " + Banana.class.getName()
- + ".std:lastevent() as b where o.price = b.price";
- EPStatement stat = admin.createEPL(epl1);
- stat.addListener(new JoinUnidirectionalListener());
- EPRuntime runtime = epService.getEPRuntime();
- Orange o1 = new Orange();
- o1.setPrice(1);
- System.out.println("Send Orange1");
- runtime.sendEvent(o1);
- Banana b1 = new Banana();
- b1.setPrice(1);
- System.out.println("Send Banana1");
- runtime.sendEvent(b1);
- Banana b2 = new Banana();
- b2.setPrice(2);
- System.out.println("Send Banana2");
- runtime.sendEvent(b2);
- Orange o2 = new Orange();
- o2.setPrice(2);
- System.out.println("Send Orange2");
- runtime.sendEvent(o2);
- }
- }
执行结果:
- Send Orange1
- Send Banana1
- Send Banana2
- Send Orange2
- Orange price=2, Banana price=2
可以看到o1和b1的price相等,o2和b2的price相等。o1先于b1进入引擎,由于o1进入时发现没有满足条件的Banana事件,所以什么输出也没有。之后b1进入了,因为满足条件的o1已经移除了,所以也是没有输出。b2先于o2进入引擎,被引擎暂存了起来,然后o2进入后,立刻进行join条件判断,发现暂存的b2的price相等,所以触发了listener并输出满足条件的这两个对象。
unidirectional使用很简单,但是也有其限制:
1.在一个join句子中,unidirectional关键字只能用于一个事件流。
2.用unidirectional修饰的事件流,不能通过esper的查询api查出来,因为该事件流是无状态的,不会暂存在引擎中,所以就没法查了。(关于查询api,后面的章节会详说)
3.使用了unidirectional修饰的事件流就不能再用data window或者view修饰了,也就是说他们是互斥的。
Esper学习之九:EPL语法(五)的更多相关文章
- 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,已经写了三篇了,预估计了一下,除了今天这篇,后面还有5篇左右.大家可别嫌多,官方的文档对EPL的讲解有将近140页,我已经尽量将废话都干掉了,再配合我附上的例子,看我的10篇文章比那140 ...
- Spring学习(十九)----- Spring的五种事务配置详解
前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识.通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的. ...
- Esper学习之十二:EPL语法(八)
今天的内容十分重要,在Esper的应用中是十分常用的功能之一.它是一种事件集合,我们可以对这个集合进行增删查改,所以在复杂的业务场景中我们肯定不会缺少它.它就是Named Window. 由于本篇篇幅 ...
- Esper学习之十一:EPL语法(七)
上一篇说到了EPL如何访问关系型数据库这种数据源,实际上别的数据源,比如:webservice.分布式缓存.非关系型数据库等等,Esper提供了统一的数据访问接口.然后今天会讲解如何创建另外一种事件类 ...
- Esper学习之四:Context
上周末打球实在太累了,就没来得及更新,只是列了个提纲做做准备,发现Context还是有很多内容的.结果也花了不少时间才写完,所以这篇需要各位慢慢消化,并且最好多写几个例子加深理解. 如果有不了解Esp ...
- Esper学习之三:进程模型
之前对Esper所能处理的事件结构进行了概述,并结合了例子进行讲解,不清楚的同学请看Esper学习之二:事件类型.今天主要为大家解释一下Esper是怎么处理事件的,即Esper的进程模型. 1.Upd ...
随机推荐
- WAS6.1连接SQL Server2008数据库连接池配置
原文链接:http://www.cnblogs.com/lyr2012/archive/2012/06/13/2547804.html 说明:该步骤只适用与websphere 6.1.0.15之前的版 ...
- PHP进阶。
老手段,百度“PHP进阶” 不过,今天运气不错,搜到一个“PHP特级内容讲解”,地址是:http://wenku.baidu.com/course/view/fd8e591b6bd97f192279e ...
- Hadoop分布式集群部署(单namenode节点)
Hadoop分布式集群部署 系统系统环境: OS: CentOS 6.8 内存:2G CPU:1核 Software:jdk-8u151-linux-x64.rpm hadoop-2.7.4.tar. ...
- 如何在linux中批量建立用户并设置随机密码
Ubuntu是基于linux的免费开源操作系统,同时也是真正意义上的“多任务多用户”操作系统,既然是多用户系统,自然就涉及到创建多个用户的问题.同时由于Ubuntu系统中的root用户具有最高权限,无 ...
- C# 获取listview中选中一行的值
首先必须要判断listView1.SelectedItems.Count>0或是listview1.SelectedIndices.Count>0,否则第一次点击会选不中.其次,好像ite ...
- 近期全国各地联通线路无法访问OA的解决方案
最近有多地区使用联通线路的用户无法访问easyradius控制台,即oa.ooofc.com,其主要的原因是由于联通的DNS解析错误,导致的 oa.ooofc.com的解析IP是115.239.252 ...
- touch事件的分发和消费机制
Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...
- 9 云计算系列之Cinder的安装与NFS作为cinder后端存储
preface 在前面我们知道了如何搭建Openstack的keystone,glance,nova,neutron,horizon这几个服务,然而在这几个服务中唯独缺少存储服务,那么下面我们就学习块 ...
- in语句导致查询很慢
1.表A,表B,表C.其中A中的主键是B的外键,一对多的关系:B的主键是C的外键,一对多的关系.最终想查出所有符合条件的C. 原因:开发人员将A表数据先查出来,放到list中,然后用list作为in的 ...
- 【转】Java中的多线程学习大总结
多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程 ...