

# 检测表格中的模式

搜索一组事件模式是一种常见的用例，尤其是在数据流的情况下。Flink 附带了一个[复杂事件处理(CEP)库](//ci.apache.org/projects/flink/flink-docs-release-1.7/dev/libs/cep.html)，允许在事件流中进行模式检测。此外，Flink 的 SQL AP I提供了一种表达查询的关系方式，该方法具有大量内置函数和基于规则的优化，可以直接使用。

2016年12月，国际标准化组织（ISO）发布了 SQL 标准的新版本，其中包括[_SQL 中的 Row 模式识别_]([ISO/IEC TR 19075-5:2016](https://standards.iso.org/ittf/PubliclyAvailableStandards/c065143_ISO_IEC_TR_19075-5_2016.zip))。它允许Flink使用 `MATCH_RECOGNIZE` 子句整合 CEP 和 SQL API，以便在 SQL 中进行复杂事件处理。



`MATCH_RECOGNIZE` 子句启用以下任务：

*   数据的逻辑分区和排序使用 `PARTITION BY` 和 `ORDER BY` 子句。
*   使用 `PATTERN` 子句定义要搜索的行的模式。这些模式使用类似于正则表达式的语法。
*   行模式变量的逻辑组件在 `DEFINE` 子句中指定。
*   在 `MEASURES` 子句中定义 measures，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` 子句的实现是完整标准的一个子集。仅支持以下记录的那些部分功能。由于开发仍处于早期阶段，请同时查看[known limitations](#known-limitations)。

## 介绍和实例

### 安装指南

模式识别功能在内部使用 Apache Flink 的 CEP 库。为了能够使用 `MATCH_RECOGNIZE` 子句，需要将库添加为 Maven 项目的依赖项。



```
<dependency>
  <groupId>org.apache.flink</groupId>
  <artifactId>flink-cep_2.11</artifactId>
  <version>1.7.1</version>
</dependency>
```



或者，您也可以将依赖项添加到集群类路径（有关详细信息，请参阅[依赖项部分](//ci.apache.org/projects/flink/flink-docs-release-1.7/projectsetup/dependencies.html)）。

如果要在 SQL 客户端中使用 `MATCH_RECOGNIZE` 子句，则不必执行任何操作，因为默认情况下包含所有依赖项。

### SQL 语义

每个 `MATCH_RECOGNIZE` 查询都包含以下子句：

*   [PARTITION BY](#partitioning) - 定义表的逻辑分区; 类似于 `GROUP BY` 操作。
*   [ORDER BY](#order-of-events) - 指定如何排序传入的行; 这是必不可少的，因为模式取决于排序。
*   [MEASURES](#define--measures) - 定义子句的输出; 类似于 `SELECT` 子句。
*   [ONE ROW PER MATCH](#output-mode) - 输出模式，定义每个匹配应生成多少行。
*   [AFTER MATCH SKIP](#after-match-strategy) - 指定下一个匹配的开始位置; 这也是一种控制单个事件属于多少个不同匹配的方法。
*   [PATTERN](#defining-pattern) - 允许使用类似 _正则表达式_ 的语法构造将要搜索的模式。
*   [DEFINE](#define--measures) -  此部分定义（匹配）模式变量必须满足的条件。



注意，目前 `MATCH_RECOGNIZE` 子句只能应用于[追加表(append table)](dynamic_tables.html#update-and-append-queries)。此外，它总是生成一个追加表。

### 例子

对于我们的示例，我们假设已经注册了表格 `Ticker`。该表包含特定时间点的股票价格。

该表具有以下 schema：



```
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` 的输入数据。ticker看起来类似于下表，其中连续追加行。



```
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 的行，它小于最后一行的 price，最后一行的 price 满足 `PRICE_DOWN` 条件。对于初始情况或当没有满足 `PRICE_DOWN` 条件的最后一行时，行的 price 应该小于模式中前一行的价格（由 `START_ROW` 引用）。

模式变量 `PRICE_UP` 定义为 `price` 行，它大于最后一行的 `price`，它满足 `PRICE_DOWN` 条件。

此查询为每个期间生成一个汇总行，其中股票的价格不断下降。

输出行的确切表示在查询的 `MEASURES` 部分中定义。输出行数由 `ONE ROW PER MATCH` 输出模式定义。



```
 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` 子句将被转换为非并行operator以确保全局排序。

## 事件顺序

Apache Flink 允许根据时间搜索模式; [处理时间或事件时间](time_attributes.html)。

在事件时间的情况下，事件在传递到内部模式状态机(the internal pattern state machine)之前进行排序。因此，无论行追加到表的顺序如何，生成的输出都是正确的。按照每行中包含的时间指定的顺序的评估模式。

`MATCH_RECOGNIZE` 子句假定时间属性具有升序排序作为 `ORDER BY` 子句的第一个参数。

对于示例 `Ticker` 表，定义如 `ORDER BY rowtime ASC, price DESC` 有效，但 `ORDER BY price, rowtime` 或 `ORDER BY rowtime DESC, price ASC` 无效


## Define 和 Measures

`DEFINE` 和 `MEASURES` 关键字与简单 SQL 查询中的 `WHERE` 和 `SELECT` 子句具有相似的含义。

`MEASURES` 子句定义将包含在匹配模式的输出中的内容。它可以投影列并定义表达式以进行评估。生成的行数取决于[输出模式](#output-mode)设置。

`DEFINE` 子句指定行必须满足的条件才能被分类到相应的[模式变量](#defining-pattern)。如果没有为模式变量定义条件，则将使用默认条件，对于每一行，该条件的计算结果为 `true`。

有关可在这些子句中使用的表达式的更详细说明，请查看[event stream navigation](#pattern-navigation)部分。

## 定义模式

`MATCH_RECOGNIZE` 子句允许用户使用功能强大且富有表现力的语法搜索事件流中的模式，该语法与广泛使用的正则表达式语法类似。

每个模式都是由基本构建块构建的，称为 _模式变量(pattern variables)_，可以应用 operators（量词和其他修饰符）。整个模式必须括在括号中。

示例模式可能如下所示：



```
PATTERN (A B+ C* D)
```



可以使用以下运算符：

*   _Concatenation_ - 像`(A B)`这样的模式意味着A和B之间的连续性是严格的。因此，不存在未映射到A或B之间的行。
*   _Quantifiers_ - 修改可以映射到模式变量的行数。
    *   `*` — _0_ 行或更多行
    *   `+` — _1_ 行或更多行
    *   `?` — _0_ 或 _1_ 行
    *   `{ n }` — 正好是 _n_ 行（_n &gt; 0_）
    *   `{ n, }` — _n_ 行或更多行（_n ≥ 0_）
    *   `{ n, m }` — _n_ 和 _m_（含）行之间（_0≤n≤m，0 &lt; m_）
    *   `{ , m }` — 介于 _0_ 和 _m_（含）行之间（_m &gt; 0_）

注意不支持可能产生空匹配的模式。这种模式的示例是 `PATTERN (A*)`，`PATTERN (A? B*)`，`PATTERN (A{0,} B{0,} C*)`等。

### Greedy 和 Reluctant 量词

每个量词可以是 _greedy（贪婪）_（默认行为）或 _reluctant（不情愿）_ 的。贪心量词（Greedy quantifiers）试图匹配尽可能多的行，而不情愿的量词试图尽可能少地匹配。

为了说明差异，可以使用查询查看以下示例，其中将 Greedy（贪心）量词应用于 `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*` 应该是 reluctant（不情愿的），将产生：



```
 symbol   lastPrice
======== ===========
 XYZ      13
 XYZ      16
```



模式变量 `B` 仅匹配价格为 `12` 的行，而不是吞噬价格为 `12`，`13` 和 `14` 的行。

注意不可能将（greedy）贪婪量词用于模式的最后一个变量。因此，不允许像 `(A B*)` 那样的模式。通过引入具有 `B` 的否定条件的人工状态（例如`C`），可以很容易地解决这个问题。因此，可以使用如下查询：



```
PATTERN (A B* C)
DEFINE
    A AS condA(),
    B AS condB(),
    C AS NOT condB()
```



注意目前不支持可选的不情愿量词（`A??` 或 `A{0,1}?`）。

## 输出模式

_输出模式(output mode)_ 描述了每次找到的匹配应该发出的行数。SQL标准描述了两种模式：

*   `每次匹配所有行（ALL ROWS PER MATCH）`
*   `每次匹配一行（ONE ROW PER MATCH）`。

目前，唯一支持的输出模式是 `ONE ROW PER MATCH`，它将始终为每个找到的匹配生成一个输出概要行。

输出行的 schema，按照特定顺序显示 `[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` 子句允许在（可能）匹配模式的行列表中导航。

这部分讨论声明条件或产生输出结果导航。

### 模式变量引用

_模式变量引用（pattern variable reference）_ 允许一组行映射一个特定的模式变量引用在 `DEFINE` 或 `MEASURES` 子句中。

例如，如果我们尝试将当前行与 `A` 匹配，则表达式 `A.price` 描述了到目前为止映射到 `A` 加上当前行的一组行。如果 `DEFINE`/`MEASURES` 子句中的表达式需要单行（例如 `A.price` 或 `A.price &gt; 10`），它将选择属于相应集的最后一个值。

如果没有指定模式变量（例如`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` - 传入行的 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 | -&gt; A | #1 | - | - | 10 | - | - | - |
| #2 | 15 | -&gt; B | #1 | #2 | #1, #2 | 10 | 15 | 25 | 15 |
| #3 | 20 | -&gt; B | #1 | #2, #3 | #1, #2, #3 | 10 | 20 | 45 | 35 |
| #4 | 31 | -&gt; 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` 中所有行的总和超过指定的阈值。

注意请注意，尚不支持 `SUM` 等聚合。它们仅用于解释。

### 逻辑偏移

_逻辑偏移(Logical offsets)_ 启用映射到特定模式变量的事件中的导航。这可以用两个相应的函数表示：

| Offset 函数 | 描述 |
| --- | --- |
|```LAST(variable.field, n)``` | 返回映射到变量的第 _n_ 个 _最后_ 一个元素的事件的字段值。计数从映射的最后一个元素开始。 |
|```FIRST(variable.field, n)``` | 从映射到变量的第 _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 | -&gt; A |  |  |  |
| 15 | -&gt; B | null | null | 注意：`LAST(A.price, 1)` 是 null 因为没有值映射到 `B`。 |
| 20 | -&gt; B | 15 | null |  |
| 31 | -&gt; B | 20 | 15 |  |
| 35 |  | 31 | 20 | 没有映射因为 `35 &lt; 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 | -&gt; A |  |  |
| 15 | -&gt; B |  |  |
| 20 | -&gt; C | 15 | `LAST(price, 1)` 被评估为映射到 `B` 变量的行的价格。 |

如果第二行没有映射到 `B` 变量，我们将得到以下结果：

| price | Classifier | LAST(price, 1) | Comment |
| --- | --- | --- | --- |
| 10 | -&gt; A |  |  |
| 20 | -&gt; 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` - 继续在匹配的起始行之后的下一行开始搜索新的匹配。
*   `SKIP TO LAST variable` - 在映射到指定模式变量的最后一行恢复模式匹配。
*   `SKIP TO FIRST variable` - 在映射到指定模式变量的第一行恢复模式匹配。

这也是一种指定单个事件可以属于多少匹配的方法。例如，使用 `SKIP PAST LAST ROW` 策略，每个事件最多只能属于一个匹配项。

#### 例子

为了更好地理解这些策略之间的差异，可以查看以下示例。

对于以下输入行：



```
 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` 的所有行的价格总和以及整个匹配的第一个和最后一个时间戳。

请注意，尚不支持 `SUM` 等聚合。它们仅用于解释。

查询将根据使用的 `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`

此组合将产生运行时异常，因为总是会尝试启动最后一个匹配的新匹配。 这将产生无限循环，因此被禁止。

必须记住，在 `SKIP TO FIRST/LAST variable` 变量策略的情况下，可能没有行映射到该变量（例如，对于模式 `A*`）。在这种情况下，将抛出运行时异常，因为标准需要有效行来继续匹配。

### 控制内存消耗

在编写 `MATCH_RECOGNIZE` 查询时，内存消耗是一个重要的考虑因素，因为潜在匹配的空间是以类似于广度的方式（a breadth-first-like manner）构建的。考虑到这一点，必须确保模式可以完成。优选地，具有映射到匹配的合理数量的行，因为它们必须适合内存。

例如，模式不能有没有上限的量词，接受访问每个单行（row）。这样的模式可能如下所示：



```
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
```



或者通过使用[不情愿的量词(reluctant quantifier)](#greedy--reluctant-quantifiers)：



```
PATTERN (A B+? C)
DEFINE
  A as A.price > 10,
  C as C.price > 20
```



请注意，`MATCH_RECOGNIZE` 子句不使用已配置的[状态保留时间(state retention time)](query_configuration.html#idle-state-retention-time)。 截至目前，也没有可能在模式上定义时间限制，因为在SQL标准中没有这种可能性。社区正在为该功能设计正确的语法。

## 已知限制

Flink 的 `MATCH_RECOGNIZE` 子句的实现是一项持续的工作，并且还不支持SQL标准的某些功能。

不支持的功能包括：

*   模式表达式：
    *   模式组（Pattern groups）- 这意味着例如量词不能应用于模式的子序列。因此，`(A (B C)+)` 不是有效模式。
    *   改变（Alterations）- 像 `PATTERN((A B | C D) E)` 这样的模式，这意味着在寻找 `E` 行之前必须找到子序列 `A B` 或 `C D`.
    *   `PERMUTE` 运算符 - 相当于它应用于的所有变量的排列，例如 `PATTERN (PERMUTE (A, B, C))` = `PATTERN (A B C | A C B | B A C | B C A | C A B | C B A)`。
    *   Anchors -  `^, $` 表示分区的开始/结束，那些在流上下文中没有意义，并且不受支持。
    *   排除（Exclusion）-  `PATTERN ({- A -} B)` 意味着将查找 `A` 但不参与输出。这仅适用于 `ALL ROWS PER MATCH` 模式。
    *   不情愿的可选量词（Reluctant optional quantifier）- `PATTERN A??` 只支持贪婪的可选（greedy）量词。
*   `ALL ROWS PER MATCH` 输出模式 - 为参与创建匹配的每一行产生输出行。这也意味着：
    *   `MEASURES` 子句唯一支持的语义是 `FINAL`
    *   尚未支持 `CLASSIFIER` 函数，该函数返回行映射到的模式变量。
*   `SUBSET` - 允许创建模式变量的逻辑组并在 `DEFINE` 和 `MEASURES` 子句中使用这些组。
*   物理偏移（Physical offsets）- `PREV/NEXT`，它对所有看到的事件进行索引，而不仅仅是那些映射到模式变量的事件（如在[逻辑偏移](#logical-offsets)情况下）。
*   提取时间属性（Extracting time attributes） - 目前无法为后续基于时间的操作获取时间属性。
*   聚合（Aggregates）- 在 `MEASURES` 和 `DEFINE` 子句中不能使用聚合。
*   用户定义的函数不能在 `MATCH_RECOGNIZE` 中使用。
*   仅支持SQL `MATCH_RECOGNIZE`。表API中没有等价物。
