Oracle SQL - CEP Syntax
CEP is a major real-time processing capability. Mature systems such as Siddhi and EsperTech have their own syntax; incorporating CEP into SQL makes for an interesting alternative.
Translated from Chinese with AI · Read the original
CEP is naturally an important capability in real-time processing. Mature frameworks such as Siddhi on GitHub and EsperTech implement it with their own developer-facing syntax. Wouldn’t it be interesting to add CEP to the versatile SQL language?
See the Oracle documentation. The syntax explanations and examples in this post all come from the official documentation.
Quick Overview
Start with a simple example: the following chart shows real-time stock prices.
We want to detect the X-Y-W-Z pattern (fall-rise-fall-rise) in real time and obtain its start and end points. The SQL below contains quite a few unfamiliar constructs, doesn’t it?
Let’s examine them one by one.
SELECT T.A, T.lastZFROM STREAMMATCH_RECOGNIZE ( MEASURES A, last(Z) as lastZ PATTERN(A W+ X+ Y+ Z+) DEFINE W as W.price < prev(W.price), X as X.price > prev(X.price), Y as Y.price < prev(Y.price), Z as Z.price > prev(Z.price)) as TMATCH_RECOGNIZE
This identifies a CEP operation, generally written as SELECT … FROM S MATCH_RECOGNIZE ( …. ) as T WHERE ….
MEASURES
Produces the measures of T. Measures can refer to events declared in DEFINE, using values such as Z.price in the example or UDAP functions such as sum.
PATTERN
The example’s (A W+ X+ Y+ Z+) specifies a strict event sequence: A can be followed by N W events, the last W by N X events, the last X by N Y events, and the last Y by N Z events.
| Greedy | Reluctant | Meaning |
|---|
- | *? | Match 0 to N events
- | +? | Match 1 to N events ? | ?? | Match 0 or 1 event
For example, in *(A B C)**, if an event satisfies both B and C in DEFINE, greedy matching assigns it to B. If B uses the reluctant *? quantifier, it is assigned to C.
Regular expressions such as (A B+ | C) are also supported.
DEFINE
Defines events and their business conditions. For example, W as W.price < prev(W.price) requires a W event’s price to be below that of the previous W event.
PARTITION BY
Partitions data, similarly to window functions. To partition the earlier stock chart by industry, the SQL can be written as follows:
PARTITION BY A.industryMEASURES A, last(Z) as lastZPATTERN(A W+ X+ Y+ Z+)DEFINE W as W.price < prev(W.price), X as X.price > prev(X.price), Y as Y.price < prev(Y.price), Z as Z.price > prev(Z.price)DURATION
Suppose you want to avoid excessive oscillation: within 10 minutes, only the W, X, Y, Z trend should occur, with no further turning points after it completes. You can require that no other events intervene during those 10 minutes.
MEASURES A, last(Z) as lastZinclude timer events // 和duration 10配合使用PATTERN(A W+ X+ Y+ Z+)duration 10DEFINE W as W.price < prev(W.price), X as X.price > prev(X.price), Y as Y.price < prev(Y.price), Z as Z.price > prev(Z.price)WITHIN
Suppose you do not want to wait a whole day for the trend: it is valid only if the conditions are met within 10 minutes. Note that WITHIN and WITHIN INCLUSIVE differ in whether they include the boundary.
MEASURES A, last(Z) as lastZPATTERN(A W+ X+ Y+ Z+)DEFINE W as W.price < prev(W.price), X as X.price > prev(X.price), Y as Y.price < prev(Y.price), Z as Z.price > prev(Z.price)ALL MATCHES
On the stock chart, any point along X’s descending segment satisfies DEFINE. ALL MATCHES includes all of them. Without ALL MATCHES, after a match the processor automatically advances to the position of its final event.