關於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
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]
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 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
我個人推薦第二種寫法,通過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
如果不顯示指定rstream,則insert into只允許istream的事件流進入Computer。如果指定爲irstream,那麼進入的和移除的Asus都會進入到Computer。
上面的例子都是指定了insert into裏事件流會有什麼屬性,如果不指定會是什麼結果呢?請看例句:
- insert into Computer select * from 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
- insert into Computer select l.* from Asus as a, Lenovo as l
- // 將Lenovo事件轉換後輸入Computer
- insert into Computer select Converter.convert(l) from Lenovo as l
3.3 Decorated Events
之前所見到的不是將事件流整體輸入insert,就是將事件流的部分屬性輸入insert。實際上可以將事件流整體和事件流屬性組成的複雜表達式一起放入insert。示例如下:
- insert into Computer select *, size*price as sp from Asus
- // 第一個*表示Asus,size*price的*表示乘法,兩者互不影響
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
- /**
- *
- * @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
對於Auto的JavaBean,我們可以發現它包含一個有參數的構造函數且沒有屬性對應的set方法,在carToAutoEpl中,select的內容並沒有和屬性名稱對應起來。這種寫法確實是正確的,正因爲Auto中定了含參的構造函數,才使得select可以寫的更隨意。但是一定要記住,構造函數裏的參數順序一定要和select中的屬性的數據類型對應起來,如果這裏把name和size互換位置,必定報錯!
對於Benz的JavaBean,可以看到每個屬性都有對應的set方法,而沒有含參的構造函數,所以select中屬性的名稱需要as來設置別名。當然,像benzEpl2那種寫法,同樣可以避免select中設置別名。
一句話總結,如果JavaBean中有含參的構造函數,EPL中不需要顯示寫出屬性名稱。如果沒有構造函數,那麼必須包含set方法,且select中要寫出具體的屬性。這幾種寫法各有各的好處,大家使用時可針對具體的情況選擇性使用。
今天的內容總的來說是比較輕鬆的,insert into也算是今天的重點,希望大家好好學習,用處可是大大滴有哦。