本文翻译自官网:Detecting Patterns in Tables Beta https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/streaming/match_recognize.html
搜索一组事件模式是一种常见的用例,尤其是在数据流的情况下。Flink带有一个复杂的事件处理(CEP)库 ,该库允许在事件流中进行模式检测。此外,Flink的SQL API提供了一种关系查询方式,该查询具有一系列内置函数和基于规则的优化来表达查询,这些查询可以直接使用。
2016年12月,国际标准化组织(ISO)发布了SQL版本的新版本,其中包括SQL中的行模式识别 (ISO / IEC TR 19075-5:2016)。它允许Flink使用该MATCH_RECOGNIZE
子句合并CEP和SQL API,以在SQL中进行复杂的事件处理。
MATCH_RECOGNIZE子句启用以下任务:
- 使用 PARTITION BY和ORDER BY子句一起逻辑分区和排序数据。
- 使用
PATTERN
子句定义要搜索的行模式。这些模式使用与正则表达式相似的语法。 - 行模式变量的逻辑组件在DEFINE子句中指定。
- 在
MEASURES
子句中定义度量,这些度量是在SQL查询的其他部分中可用的表达式。
以下示例说明了基本模式识别的语法:
SELECT T.aid, T.bid, T.cid FROM MyTable MATCH_RECOGNIZE ( PARTITION BY userid ORDER BY proctime MEASURES A.id AS aid, B.id AS bid, C.id AS cid PATTERN (A B C) DEFINE A AS name = 'a', B AS name = 'b', C AS name = 'c' ) AS T
该页面将更详细地说明每个关键字,并说明更复杂的示例。
注意 :Flink 对 MATCH_RECOGNIZE
子句的实现是完整标准的子集。仅支持以下各节中记录的那些功能。由于开发仍处于早期阶段,因此请同时查看 已知的局限性。
简介与范例
安装指南
模式识别功能在内部使用Apache Flink的CEP库。为了能够使用该MATCH_RECOGNIZE
子句,需要将库作为依赖项添加到您的Maven项目中。
<dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-cep_2.11</artifactId> <version>1.9.0</version> </dependency>
或者,您也可以将依赖项添加到群集类路径中(有关更多信息,请参见 依赖项部分)。
如果要在SQL Client中使用 MATCH_RECOGNIZE
子句,则 无需执行任何操作,因为默认情况下包括所有依赖项。
SQL语义
每个MATCH_RECOGNIZE
查询都包含以下子句:
- PARTITION BY - 定义表的逻辑分区;类似于
GROUP BY
操作。 - ORDER BY - 指定应如何对进入的行进行排序;这是必不可少的,因为模式取决于顺序。
- MEASURES - 定义该条款的输出;类似于
SELECT
条款。 - ONE ROW PER MATCH - 输出模式,定义每个匹配项应产生多少行。
- AFTER MATCH SKIP - 指定下一次匹配应该在哪里开始; 这也是控制单个事件可以属于多少个不同匹配项的方法。
- PATTERN - 允许构造使用类似正则表达式的语法进行搜索的模式。
- 定义 - 本部分定义了模式变量必须满足的条件。
注意:当前,该MATCH_RECOGNIZE
子句只能应用于追加表。此外,它也总是产生一个 追加表。
例子
对于我们的示例,我们假设一个表Ticker
已被注册。该表包含特定时间点的股票价格。
该表具有以下结构:
Ticker |-- symbol: String # symbol of the stock |-- price: Long # price of the stock |-- tax: Long # tax liability of the stock |-- rowtime: TimeIndicatorTypeInfo(rowtime) # point in time when the change to those values happened
为了简化,我们仅考虑单个ACME股票的传入数据。 股票代码看起来类似于下表,其中的行被连续附加。
symbol rowtime price tax
====== ==================== ======= =======
'ACME' '01-Apr-11 10:00:00' 12 1
'ACME' '01-Apr-11 10:00:01' 17 2
'ACME' '01-Apr-11 10:00:02' 19 1
'ACME' '01-Apr-11 10:00:03' 21 3
'ACME' '01-Apr-11 10:00:04' 25 2
'ACME' '01-Apr-11 10:00:05' 18 1
'ACME' '01-Apr-11 10:00:06' 15 1
'ACME' '01-Apr-11 10:00:07' 14 2
'ACME' '01-Apr-11 10:00:08' 24 2
'ACME' '01-Apr-11 10:00:09' 25 2
'ACME' '01-Apr-11 10:00:10' 19 1
现在的任务是找到单个股票价格不断下降的时期。为此,可以编写如下查询:
SELECT * FROM Ticker MATCH_RECOGNIZE ( PARTITION BY symbol ORDER BY rowtime MEASURES START_ROW.rowtime AS start_tstamp, LAST(PRICE_DOWN.rowtime) AS bottom_tstamp, LAST(PRICE_UP.rowtime) AS end_tstamp ONE ROW PER MATCH AFTER MATCH SKIP TO LAST PRICE_UP PATTERN (START_ROW PRICE_DOWN+ PRICE_UP) DEFINE PRICE_DOWN AS (LAST(PRICE_DOWN.price, 1) IS NULL AND PRICE_DOWN.price < START_ROW.price) OR PRICE_DOWN.price < LAST(PRICE_DOWN.price, 1), PRICE_UP AS PRICE_UP.price > LAST(PRICE_DOWN.price, 1) ) MR;
该查询按 symbol 列对 Ticker 表进行分区,并按rowtime时间属性对其进行排序。
PATTERN子句指定我们对以下模式感兴趣:该模式的起始事件为START_ROW,后跟一个或多个PRICE_DOWN事件,并以PRICE_UP事件结束。 如果可以找到这样的模式,则将在最后一个PRICE_UP事件中寻找下一个模式匹配,如AFTER MATCH SKIP TO LAST子句所示。
DEFINE子句指定PRICE_DOWN和PRICE_UP事件需要满足的条件。 尽管不存在START_ROW模式变量,但它具有一个隐式条件,该条件始终被评估为TRUE。
模式变量PRICE_DOWN定义为价格小于满足PRICE_DOWN条件的最后一行价格的行。 对于初始情况或当没有满足PRICE_DOWN条件的最后一行时,该行的价格应小于该模式中前一行的价格(由START_ROW引用)。
模式变量PRICE_UP定义为价格大于满足PRICE_DOWN条件的最后一行价格的行。
该查询为股票价格连续下降的每个时期产生一个汇总行。
输出行的确切表示在查询的 MEASURES 部分中定义。 输出行数由“每行一次匹配”输出模式定义。
symbol start_tstamp bottom_tstamp end_tstamp ========= ================== ================== ================== ACME 01-APR-11 10:00:04 01-APR-11 10:00:07 01-APR-11 10:00:08
结果行描述了一个价格下跌时期,该时期始于01-APR-11 10:00:04,并在 01-APR-11 10:00:07 达到了最低价格,并在01-APR-11 10:00:08再次上涨。
分区
可以在分区数据中查找模式,例如单个股票或特定用户的趋势。这可以使用 PARTITION BY
子句来表达。该子句类似于 GROUP BY
用于聚合。
注意:强烈建议对传入的数据进行分区,因为否则该MATCH_RECOGNIZE
子句将转换为非并行运算符以确保全局排序。
事件顺序
Apache Flink允许根据时间搜索模式; 处理时间或事件时间。
如果使用事件时间,则在将事件传递到内部模式状态机之前对其进行排序。因此,无论将行附加到表的顺序如何,生成的输出都是正确的。而且按照每行中包含的时间指定的顺序评估模式。
MATCH_RECOGNIZE 子句假定一个以升序排列的时间属性作为 ORDER BY 子句的第一个参数。
对于示例 Ticker
表,ORDER BY rowtime ASC,price DESC的定义有效,但是ORDER BY price,rowtime或ORDER BY rowtime DESC,price ASC无效。
定义和度量
在简单的SQL查询中,DEFINE和MEASURES关键字的含义与WHERE和SELECT子句的含义相似。
MEASURES子句定义匹配模式的输出中将包含的内容。 它可以投影列并定义用于评估的表达式。 产生的行数取决于输出模式设置。
DEFINE 子句指定行必须满足的条件才能被分类为相应的模式变量。 如果未为模式变量定义条件,则将使用默认条件,每一行的条件都为true。
有关可以在这些子句中使用的表达式的更详细说明,请查看事件流导航部分。
聚合
可以在DEFINE和MEASURES子句中使用聚合。 内置和自定义用户定义函数均受支持。
聚合函数应用于映射到匹配项的行的每个子集。为了了解如何评估这些子集,请查看事件流导航 部分。
以下示例的任务是找到报价器的平均价格未低于特定阈值的最长时间段。 它显示了可表达的MATCH_RECOGNIZE如何通过聚合来实现。 可以通过以下查询执行此任务:
SELECT * FROM Ticker MATCH_RECOGNIZE ( PARTITION BY symbol ORDER BY rowtime MEASURES FIRST(A.rowtime) AS start_tstamp, LAST(A.rowtime) AS end_tstamp, AVG(A.price) AS avgPrice ONE ROW PER MATCH AFTER MATCH SKIP PAST LAST ROW PATTERN (A+ B) DEFINE A AS AVG(A.price) < 15 ) MR;
给定此查询和以下输入值:
symbol rowtime price tax
====== ==================== ======= =======
'ACME' '01-Apr-11 10:00:00' 12 1
'ACME' '01-Apr-11 10:00:01' 17 2
'ACME' '01-Apr-11 10:00:02' 13 1
'ACME' '01-Apr-11 10:00:03' 16 3
'ACME' '01-Apr-11 10:00:04' 25 2
'ACME' '01-Apr-11 10:00:05' 2 1
'ACME' '01-Apr-11 10:00:06' 4 1
'ACME' '01-Apr-11 10:00:07' 10 2
'ACME' '01-Apr-11 10:00:08' 15 2
'ACME' '01-Apr-11 10:00:09' 25 2
'ACME' '01-Apr-11 10:00:10' 25 1
'ACME' '01-Apr-11 10:00:11' 30 1
只要事件的平均价格不超过15,该查询就会将事件作为模式变量A的一部分进行累积。例如,这种限制发生在01-Apr-11 10:00:04。 接下来的时间段在 01-Apr-11 10:00:11 再次超过平均价格15。 因此,所述查询的结果将是:
symbol start_tstamp end_tstamp avgPrice ========= ================== ================== ============ ACME 01-APR-11 10:00:00 01-APR-11 10:00:03 14.5 ACME 01-APR-11 10:00:05 01-APR-11 10:00:10 13.5
注意:聚合可以应用于表达式,但前提是它们引用单个模式变量。因此SUM(A.price * A.tax)
是有效的,但 AVG(A.price * B.tax)
不是。
注意:不支持DISTINCT聚合。
定义模式
使用 MATCH_RECOGNIZE 子句,用户可以使用功能强大且表现力强的语法来搜索事件流中的模式,该语法有点类似于广泛的正则表达式语法。
每个模式都由称为模式变量的基本构建块构建而成,可以将运算符(量词和其他修饰符)应用于该基本变量。整个模式必须放在方括号中。
模式示例如下所示:
PATTERN (A B+ C* D)
可以使用以下运算符:
- 串联 -类似的模式
(A B)
表示A
和 B 之间严格连续。因此,不能有未映射到其间
A
或B
之间的行。 - 量词 -修改可以映射到模式变量的行数。
*
— 0行或更多行+
— 1行或更多行?
— 0或1行{ n }
— 正好n行(n> 0){ n, }
— n行或更多行(n≥0){ n, m }
— 在n和m(含)行之间(0≤n≤m,0 <m){ , m }
— 0至m(含)行之间(m> 0)
注意:不支持可能产生空匹配的模式。这样的模式的实例是PATTERN (A*)
,PATTERN (A? B*)
, PATTERN (A{0,} B{0,} C*)
,等。
Greedy & Reluctant的量词
每个量词可以是贪婪的(默认行为)或懒惰的。贪婪的量词尝试匹配尽可能多的行,而懒惰的量词则尝试匹配尽可能少的行。
为了说明差异,可以通过查询查看以下示例,其中将贪婪量词应用于变量B:
SELECT * FROM Ticker MATCH_RECOGNIZE( PARTITION BY symbol ORDER BY rowtime MEASURES C.price AS lastPrice ONE ROW PER MATCH AFTER MATCH SKIP PAST LAST ROW PATTERN (A B* C) DEFINE A AS A.price > 10, B AS B.price < 15, C AS C.price > 12 )
假设我们有以下输入:
symbol tax price rowtime ======= ===== ======== ===================== XYZ 1 10 2018-09-17 10:00:02 XYZ 2 11 2018-09-17 10:00:03 XYZ 1 12 2018-09-17 10:00:04 XYZ 2 13 2018-09-17 10:00:05 XYZ 1 14 2018-09-17 10:00:06 XYZ 2 16 2018-09-17 10:00:07
上面的模式将产生以下输出:
symbol lastPrice ======== =========== XYZ 16
将B *修改为B *?的同一查询,这意味着B *应该是懒惰的,将生成:
symbol lastPrice ======== =========== XYZ 13 XYZ 16
模式变量B仅匹配价格为12的行,而不是吞噬价格为12、13和14的行。
注意:不可以对模式的最后一个变量使用贪婪量词。 因此,不允许使用类似(A B *)的模式。 可以通过引入条件为B的人工状态(例如C)来轻松解决此问题。因此,您可以使用类似以下的查询:
PATTERN (A B* C)
DEFINE
A AS condA(),
B AS condB(),
C AS NOT condB()
注意:目前不支持可选的懒惰量词(A??
或 A{0,1}?
)。
时间限制
特别是对于流使用情况,通常需要在给定的时间段内完成模式。这允许限制Flink必须在内部保持的总体状态大小,即使在贪婪的量词的情况下也是如此。
因此,Flink SQL支持附加的(非标准SQL)WITHIN
子句,用于定义模式的时间约束。可以在PATTERN
子句之后定义该子句,该子句的间隔为毫秒。
如果潜在匹配的第一个事件和最后一个事件之间的时间长于给定值,则此类匹配将不会附加到结果表中。
注意:通常推荐使用该 WITHIN
子句,因为它有助于Flink进行有效的内存管理。一旦达到阈值,即可修剪基础状态。
注意:但是,WITHIN
子句不是 SQL 标准的一部分。建议的处理时间限制的方法将来可能会更改。
以下示例查询说明了 WITHIN 子句的用法:
SELECT * FROM Ticker MATCH_RECOGNIZE( PARTITION BY symbol ORDER BY rowtime MEASURES C.rowtime AS dropTime, A.price - C.price AS dropDiff ONE ROW PER MATCH AFTER MATCH SKIP PAST LAST ROW PATTERN (A B* C) WITHIN INTERVAL '1' HOUR DEFINE B AS B.price > A.price - 10 C AS C.price < A.price - 10 )
该查询检测到在1小时的间隔内价格下降了10。
假设该查询用于分析以下行情清单数据:
symbol rowtime price tax
====== ==================== ======= =======
'ACME' '01-Apr-11 10:00:00' 20 1
'ACME' '01-Apr-11 10:20:00' 17 2
'ACME' '01-Apr-11 10:40:00' 18 1
'ACME' '01-Apr-11 11:00:00' 11 3
'ACME' '01-Apr-11 11:20:00' 14 2
'ACME' '01-Apr-11 11:40:00' 9 1
'ACME' '01-Apr-11 12:00:00' 15 1
'ACME' '01-Apr-11 12:20:00' 14 2
'ACME' '01-Apr-11 12:40:00' 24 2
'ACME' '01-Apr-11 13:00:00' 1 2
'ACME' '01-Apr-11 13:20:00' 19 1
该查询将产生以下结果:
symbol dropTime dropDiff
====== ==================== =============
'ACME' '01-Apr-11 13:00:00' 14
结果行表示价格从15
(在01-Apr-11 12:00:00
)下降到1
(在 01-Apr-11 13:00:00
)。该dropDiff
列包含价格差距。
请注意,即使价格也下降了较高的值,例如11
(01-Apr-11 10:00:00
和01-Apr-11 11:40:00之间的 ),这两个事件的时间差大于1小时。因此,他们不会产生匹配。
输出模式
该输出模式描述了每一个找到的匹配应发出多少行。SQL标准描述了两种模式:
ALL ROWS PER MATCH
ONE ROW PER MATCH
。
当前,唯一受支持的输出模式是ONE ROW PER MATCH
始终为每个找到的匹配项生成一个输出摘要行。
输出行的模式将是特定顺序的[partitioning columns] + [measures columns]
串联 。
以下示例显示了定义为的查询的输出:
SELECT * FROM Ticker MATCH_RECOGNIZE( PARTITION BY symbol ORDER BY rowtime MEASURES FIRST(A.price) AS startPrice, LAST(A.price) AS topPrice, B.price AS lastPrice ONE ROW PER MATCH PATTERN (A+ B) DEFINE A AS LAST(A.price, 1) IS NULL OR A.price > LAST(A.price, 1), B AS B.price < LAST(A.price) )
对于以下输入行:
symbol tax price rowtime ======== ===== ======== ===================== XYZ 1 10 2018-09-17 10:00:02 XYZ 2 12 2018-09-17 10:00:03 XYZ 1 13 2018-09-17 10:00:04 XYZ 2 11 2018-09-17 10:00:05
该查询将产生以下输出:
symbol startPrice topPrice lastPrice ======== ============ ========== =========== XYZ 10 13 11
模式识别按symbol
列划分。即使未在MEASURES
子句中明确提及,分区列也将添加到结果的开头。
模式导航
DEFINE
和MEASURES
子句允许对于(可能)匹配的模式的行列表中进行导航。
本节讨论用于声明条件或产生输出结果的导航。
模式变量引用
模式变量引用允许引用DEFINE或MEASURES子句中映射到特定模式变量的一组行。
例如,如果我们尝试将当前行与A匹配,则表达式A.price将描述一组到目前为止已映射到A的行,再加上当前行。如果DEFINE / MEASURES子句中的表达式需要一行(例如A. price或A.price> 10),它将选择属于相应集合的最后一个值。
如果未指定任何模式变量(例如SUM(price)
),则表达式引用默认模式变量*
,该默认模式变量引用模式中的所有变量。换句话说,它创建了一个列表,列出了到目前为止映射到任何变量的所有行以及当前行。
如果未指定任何模式变量(例如SUM(price)),则表达式引用默认模式变量 *,该默认模式变量引用模式中的所有变量。 换句话说,它创建了一个列表,列出了到目前为止映射到任何变量的所有行以及当前行。
例子
对于更详尽的示例,可以看看以下模式和相应条件:
PATTERN (A B+) DEFINE A AS A.price > 10, B AS B.price > A.price AND SUM(price) < 100 AND SUM(B.price) < 80
下表描述了如何为每个传入事件评估这些条件。
该表包括以下列:
#
- 行标识符唯一地识别在列表中的传入行[A.price]
/[B.price]
/[price]
。price
- 传入行的价格。[A.price]
/[B.price]
/[price]
- 描述在DEFINE子句中用于评估条件的行的列表。Classifier
- 当前行的分类器,指示该行映射到的模式变量。A.price
/B.price
/SUM(price)
/SUM(B.price)
- 描述这些表达式求值后的结果。
# | price | Classifier | [A.price] | [B.price] | [price] | A.price | B.price | SUM(price) | SUM(B.price) |
---|---|---|---|---|---|---|---|---|---|
#1 | 10 | -> A | #1 | - | - | 10 | - | - | - |
#2 | 15 | -> B | #1 | #2 | #1, #2 | 10 | 15 | 25 | 15 |
#3 | 20 | -> B | #1 | #2, #3 | #1, #2, #3 | 10 | 20 | 45 | 35 |
#4 | 31 | -> B | #1 | #2, #3, #4 | #1, #2, #3, #4 | 10 | 31 | 76 | 66 |
#5 | 35 | #1 | #2, #3, #4, #5 | #1, #2, #3, #4, #5 | 10 | 35 | 111 | 101 |
从表中可以看出,第一行映射到模式变量A,随后的行映射到模式变量B。但是,最后一行不满足B条件,因为所有映射行SUM(price)和 B中所有行的总和超过指定的阈值。
逻辑偏移
逻辑偏移使您可以在映射到特定模式变量的事件中进行导航。这可以用两个相应的函数表示:
偏移功能 | 描述 |
---|---|
|
返回字段的从被映射到该事件的值Ñ个 最后的可变的元件。计数从映射的最后一个元素开始。 |
|
返回事件的字段值,该事件已映射到变量的第n个元素。计数从映射的第一个元素开始。
|
例子
对于更详尽的示例,可以看看以下模式和相应条件:
PATTERN (A B+) DEFINE A AS A.price > 10, B AS (LAST(B.price, 1) IS NULL OR B.price > LAST(B.price, 1)) AND (LAST(B.price, 2) IS NULL OR B.price > 2 * LAST(B.price, 2))
下表描述了如何为每个传入事件评估这些条件。
该表包括以下列:
price
- 传入行的价格。Classifier
- 当前行的分类器,指示该行映射到的模式变量。LAST(B.price, 1)
/LAST(B.price, 2)
- 描述对这些表达式求值后的结果。
price | Classifier | LAST(B.price, 1) | LAST(B.price, 2) | Comment |
---|---|---|---|---|
10 | -> A | |||
15 | -> B | null | null | Notice that LAST(A.price, 1) is null because there is still nothing mapped to B . |
20 | -> B | 15 | null | |
31 | -> B | 20 | 15 | |
35 | 31 | 20 | Not mapped because 35 < 2 * 20 . |
将默认模式变量与逻辑偏移量一起使用也可能很有意义。
在这种情况下,偏移量会考虑到目前为止映射的所有行:
PATTERN (A B? C) DEFINE B AS B.price < 20, C AS LAST(price, 1) < C.price
price | Classifier | LAST(price, 1) | Comment |
---|---|---|---|
10 | -> A | ||
15 | -> B | ||
20 | -> C | 15 | LAST(price, 1) 被评估为映射到变量B的行的价格 。 |
如果第二行未映射到B
变量,我们将得到以下结果:
price | Classifier | LAST(price, 1) | Comment |
---|---|---|---|
10 | -> A | ||
20 | -> C | 10 | LAST(price, 1) 被评估为映射到 变量A的行的价格 。 |
也可以在FIRST / LAST函数的第一个参数中使用多个模式变量引用。 这样,可以编写访问多个列的表达式。 但是,它们都必须使用相同的模式变量。 换句话说,必须在单个行中计算LAST / FIRST函数的值。
因此,可以使用LAST(A.price * A.tax)
,但LAST(A.price * B.tax)
不允许类似的表达式。
匹配后策略
AFTER MATCH SKIP
子句指定在找到完全匹配项之后从何处开始新的匹配过程。
有四种不同的策略:
SKIP PAST LAST ROW
- 在当前匹配的最后一行之后的下一行恢复模式匹配。SKIP TO NEXT ROW
- 继续搜索新的MATCH,MATCH 从 MATCH 开始行的下一行开始。SKIP TO LAST variable
-在映射到指定模式变量的最后一行恢复模式匹配。SKIP TO FIRST variable
-在映射到指定模式变量的第一行中恢复模式匹配。
这也是指定单个事件可以属于多少个匹配项的方法。例如,使用该 SKIP PAST LAST ROW
策略,每个事件最多只能属于一个MATCH。
例子
为了更好地理解这些策略之间的差异,可以看一下以下示例。
对于以下输入行:
symbol tax price rowtime ======== ===== ======= ===================== XYZ 1 7 2018-09-17 10:00:01 XYZ 2 9 2018-09-17 10:00:02 XYZ 1 10 2018-09-17 10:00:03 XYZ 2 5 2018-09-17 10:00:04 XYZ 2 17 2018-09-17 10:00:05 XYZ 2 14 2018-09-17 10:00:06
我们使用不同的策略评估以下查询:
SELECT * FROM Ticker MATCH_RECOGNIZE( PARTITION BY symbol ORDER BY rowtime MEASURES SUM(A.price) AS sumPrice, FIRST(rowtime) AS startTime, LAST(rowtime) AS endTime ONE ROW PER MATCH [AFTER MATCH STRATEGY] PATTERN (A+ C) DEFINE A AS SUM(A.price) < 30 )
该查询返回映射到A的所有行的价格的总和以及整体匹配的第一个和最后一个时间戳。
该查询将根据AFTER MATCH
所使用的策略产生不同的结果:
AFTER MATCH SKIP PAST LAST ROW
symbol sumPrice startTime endTime ======== ========== ===================== ===================== XYZ 26 2018-09-17 10:00:01 2018-09-17 10:00:04 XYZ 17 2018-09-17 10:00:05 2018-09-17 10:00:06
第一个结果与#1,#2,#3,#4行匹配。
第二个结果与#5,#6行匹配。
AFTER MATCH SKIP TO NEXT ROW
symbol sumPrice startTime endTime ======== ========== ===================== ===================== XYZ 26 2018-09-17 10:00:01 2018-09-17 10:00:04 XYZ 24 2018-09-17 10:00:02 2018-09-17 10:00:05 XYZ 15 2018-09-17 10:00:03 2018-09-17 10:00:05 XYZ 22 2018-09-17 10:00:04 2018-09-17 10:00:06 XYZ 17 2018-09-17 10:00:05 2018-09-17 10:00:06
同样,第一个结果与行#1,#2,#3,#4相匹配。
与之前的策略相比,下一个匹配项在下一个匹配项中再次包含了第2行。因此,第二个结果与行#2,#3,#4,#5相匹配。
第三个结果与行#3,#4,#5相匹配。
第四结果与行#4,#5,#6相匹配。
最后的结果与#5,#6行匹配。
AFTER MATCH SKIP TO LAST A
symbol sumPrice startTime endTime ======== ========== ===================== ===================== XYZ 26 2018-09-17 10:00:01 2018-09-17 10:00:04 XYZ 15 2018-09-17 10:00:03 2018-09-17 10:00:05 XYZ 22 2018-09-17 10:00:04 2018-09-17 10:00:06 XYZ 17 2018-09-17 10:00:05 2018-09-17 10:00:06
同样,第一个结果与行#1,#2,#3,#4相匹配。
与之前的策略相比,下一个匹配项仅包含下一个匹配项的第3行(映射到A
)。因此,第二个结果与行#3,#4,#5相匹配。
第三个结果与#4,#5,#6行匹配。
最后的结果与#5,#6行匹配。
AFTER MATCH SKIP TO FIRST A
这种组合将产生运行时异常,因为总是会尝试从上一个MATCH开始的地方开始一个新比赛。这将产生无限循环,因此被禁止。
必须记住,在采用该SKIP TO FIRST/LAST variable
策略的情况下,可能没有行映射到该变量(例如,针对模式A*
)。在这种情况下,将抛出运行时异常,因为标准要求有效行才能继续匹配。
时间属性
为了在上进行一些后续查询,MATCH_RECOGNIZE
可能需要使用时间属性。要选择这些功能,有两个功能:
功能 | 描述 |
---|---|
MATCH_ROWTIME() |
返回映射到给定模式的最后一行的时间戳。 结果属性是一个行时间属性,可以在随后的基于时间的操作(例如带时间窗口的join 和分组窗口或整个窗口聚合)中使用。 |
MATCH_PROCTIME() |
返回一个proctime属性,该属性可以在随后的基于时间的操作(例如带时间窗口的join和分组窗口或整个窗口聚合)中使用。
|
控制内存消耗
在编写MATCH_RECOGNIZE
查询时,内存消耗是一个重要的考虑因素,因为潜在匹配的空间是以类似于广度优先的方式构建的。考虑到这一点,必须确保模式可以完成。最好将合理数量的行映射到匹配项,因为它们必须适合内存。
例如,模式必须没有接受每行的上限的量词。这样的模式可能看起来像这样:
PATTERN (A B+ C) DEFINE A as A.price > 10, C as C.price > 20
该查询会将每个传入的行映射到该B
变量,因此将永远不会完成。该查询可以解决,例如,通过否定条件C
:
PATTERN (A B+ C) DEFINE A as A.price > 10, B as B.price <= 20, C as C.price > 20
或使用懒惰的量词:
PATTERN (A B+? C) DEFINE A as A.price > 10, C as C.price > 20
注意:请注意,MATCH_RECOGNIZE子句不使用配置的状态保留时间。 为此,可能需要使用WITHIN子句。
已知局限性
Flink对MATCH_RECOGNIZE
子句的实现是一项持续的工作,并且尚不支持SQL标准的某些功能。
不支持的功能包括:
- 模式表达式:
- Pattern groups - 这意味着例如:量词不能应用于模式的子序列。 因此,(A(B C)+)不是有效的模式。
- Alterations - 像PATTERN((A B | C D)E)这样的模式,这意味着在寻找E行之前必须找到子序列A B或C D。
PERMUTE
operator - 这等同于它应用于例如的所有变量的排列 模式(PERMUTE(A,B,C))=模式(A B C | A C B | B A C | B C A | C A B | C B A)。- Anchors - ^,$,表示分区的开始/结束,在流上下文中没有意义,将不被支持。
- Exclusion - PATTERN({-A-} B)表示将查找A,但不参与输出。 这仅适用于“每行所有行”模式。
- Reluctant optional quantifier - PATTERNA?? 仅支持贪婪的可选量词。
- Pattern groups - 这意味着例如:量词不能应用于模式的子序列。 因此,(A(B C)+)不是有效的模式。
ALL ROWS PER MATCH output mode
- 为参与创建匹配项的每一行产生一个输出行。这也意味着:- 该
MEASURES
子句唯一受支持的语义是FINAL
CLASSIFIER
尚不支持该参数返回将行映射到的模式变量。
- 该
SUBSET
- 这允许创建模式变量的逻辑组,并在DEFINE和MEASURES子句中使用这些组。- hysical offsets - PREV / NEXT,它索引所有可见的事件,而不是仅索引那些映射到模式变量的事件(在逻辑偏移情况下)。。
- Extracting time attributes - 当前没有可能为后续的基于时间的操作获取时间属性。
MATCH_RECOGNIZE
仅 SQL 支持。Table API中没有对应功能。- Aggregations:
- 不支持不同的聚合。
欢迎关注Flink菜鸟公众号,会不定期更新Flink(开发技术)相关的推文