diff options
| author | Alexey Pozdniakov <[email protected]> | 2025-04-21 17:12:32 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-04-21 17:12:32 +0300 |
| commit | bdd86bd5a51616198d95d056dae0a8effb148317 (patch) | |
| tree | 989d8b096d4fdef4eff23706d9d8422d1c3ef712 | |
| parent | 2530f5d7f7203e24b274c8f1083d0009f15b83a3 (diff) | |
[YQ-3985]: M_R: актуализировать документацию (#15846)
6 files changed, 734 insertions, 6 deletions
diff --git a/ydb/docs/en/core/yql/reference/syntax/select/index.md b/ydb/docs/en/core/yql/reference/syntax/select/index.md index e6f7ae50cfa..bc219d210b1 100644 --- a/ydb/docs/en/core/yql/reference/syntax/select/index.md +++ b/ydb/docs/en/core/yql/reference/syntax/select/index.md @@ -23,7 +23,8 @@ SELECT 2 + 2; The `SELECT` query result is calculated as follows: * Determine the set of input tables by evaluating the [FROM](from.md) clauses. -* Apply [SAMPLE](sample.md)/[TABLESAMPLE](sample.md) to input tables. +* Apply [MATCH_RECOGNIZE](match_recognize.md) to input tables. +* Evaluate [SAMPLE](sample.md)/[TABLESAMPLE](sample.md). * Execute [FLATTEN COLUMNS](../flatten.md#flatten-columns) or [FLATTEN BY](../flatten.md); aliases set in `FLATTEN BY` become visible after this point. {% if feature_join %} @@ -129,6 +130,7 @@ If the underlying queries have one of the `ORDER BY/LIMIT/DISCARD/INTO RESULT` o * [LIMIT OFFSET](limit_offset.md) * [SAMPLE](sample.md) * [TABLESAMPLE](sample.md) +* [MATCH_RECOGNIZE](match_recognize.md) {% if yt %} diff --git a/ydb/docs/en/core/yql/reference/syntax/select/match_recognize.md b/ydb/docs/en/core/yql/reference/syntax/select/match_recognize.md new file mode 100644 index 00000000000..b782c78b6df --- /dev/null +++ b/ydb/docs/en/core/yql/reference/syntax/select/match_recognize.md @@ -0,0 +1,363 @@ +# MATCH_RECOGNIZE + +The `MATCH_RECOGNIZE` expression performs pattern recognition in a sequence of rows and returns the found results. This functionality is important for various business areas, such as fraud detection, pricing analysis in finance, and sensor data processing. This area is known as Complex Event Processing (CEP), and pattern recognition is a valuable tool for this. An example of how `MATCH_RECOGNIZE` works is provided in the [link](#example). + +## Data processing algorithm + +The `MATCH_RECOGNIZE` expression performs the following actions: + +1. The input table is divided into non-overlapping groups. Each group consists of a set of rows from the input table with identical values in the columns listed after `PARTITION BY`. +2. Each group is ordered according to the `ORDER BY` clause. +3. Recognition of pattern from `PATTERN` is performed independently in each ordered group. +4. Pattern search in the sequence of rows is a step-by-step process: rows are checked one by one if they fit the pattern. Among all matches starting in the earliest row, the one consisting of the largest number of rows is selected. If no matches were found starting in the earliest row, the search continues starting from the next row. +5. After a match is found, the columns defined by expressions in the `MEASURES` block are calculated. +6. Depending on the `ROWS PER MATCH` mode, one or all rows for the found match are output. +7. The `AFTER MATCH SKIP` mode determines from which row the pattern recognition will resume. + +## Syntax {#syntax} + +```sql +MATCH_RECOGNIZE ( + [ PARTITION BY <partition_1> [ ... , <partition_N> ] ] + [ ORDER BY <sort_key_1> [ ... , <sort_key_N> ] ] + [ MEASURES <expression_1> AS <column_name_1> [ ... , <expression_N> AS <column_name_N> ] ] + [ ROWS PER MATCH ] + [ AFTER MATCH SKIP ] + PATTERN (<search_pattern>) + DEFINE <variable_1> AS <predicate_1> [ ... , <variable_N> AS <predicate_N> ] +) +``` + +Here is a brief description of the SQL syntax elements of the `MATCH_RECOGNIZE` expression: + +* [`DEFINE`](#define): Block for declaring variables that describe the search pattern and the conditions that rows must meet for each variable. +* [`PATTERN`](#pattern): [Regular expressions](https://en.wikipedia.org/wiki/Regular_expressions) describing the search pattern. +* [`MEASURES`](#measures): Defines the list of columns for the returned data. Each column is specified by an SQL expression for its computation. +* [`ROWS PER MATCH`](#rows_per_match): Determines the structure of the returned data and the number of rows for each match found. +* [`AFTER MATCH SKIP`](#after_match_skip): Defines the method of moving to the point of the next match search. +* [`ORDER BY`](#order_by): Determines sorting of input data. Pattern search is performed within the data sorted according to the list of columns or expressions listed in `<sort_key_1> [ ... , <sort_key_N> ]`. +* [`PARTITION BY`](#partition_by): Divides the input table according to the specified rules in accordance with `<partition_1> [ ... , <partition_N> ]`. Pattern search is performed independently in each part. + +### DEFINE {#define} + +```sql +DEFINE <variable_1> AS <predicate_1> [ ... , <variable_N> AS <predicate_N> ] +``` + +`DEFINE` declares variables that are used to describe the desired pattern defined in [`PATTERN`](#pattern). Variables are named SQL statements evaluated over the input data. The syntax of the SQL statements in `DEFINE` is the same as the SQL statements of the `WHERE` predicate. For example, the `button = 1` expression searches for rows with the value `1` in the `button` column. Any SQL expressions that can be used to perform a search, including aggregation functions (`LAST`, `FIRST`). For example, `button > 2 AND zone_id < 12` or `LAST(button) > 10`. + +In the example below, the SQL statement `A.button = 1` is declared as variable `A`. + +```sql +DEFINE + A AS A.button = 1 +``` + +{% note info %} + +`DEFINE` does not currently support aggregation functions (e.g., `AVG`, `MIN`, or `MAX`) and `PREV` and `NEXT` functions. + +{% endnote %} + +When processing each row of data, all SQL statements describing variables in `DEFINE` are calculated. When the SQL-expression describing the corresponding variable from `DEFINE` gets the `TRUE` value, such a row is labeled with the `DEFINE` variable name and added to the list of rows subject to pattern matching. + +#### **Example** {#define-example} + +When defining variables in SQL expressions, you can reference other variables: + +```sql +DEFINE + A AS A.button = 1 AND LAST(A.zone_id) = 12, + B AS B.button = 2 AND FIRST(A.zone_id) = 12 +``` + +An input data row will be computed as variable `A` if it contains a `button` column with value `1` and the last row of the set of previously matched `A` has a `zone_id` column with value `12`. The row will be computed as variable `B` if the data row contains a `button` column with value `2` and the first row of the set of previously matched variables `A` has a `zone_id` column with value `12`. + +### PATTERN {#pattern} + +```sql +PATTERN (<search_pattern>) +``` + +The `PATTERN` keyword describes the search pattern in the format derived from variables in the `DEFINE` section. The `PATTERN` syntax is similar to the one of [regular expressions](https://en.wikipedia.org/wiki/Regular_expressions). + +{% note warning %} + +If a variable used in the `PATTERN` section has not been previously described in the `DEFINE` section, it is assumed that it is always `TRUE`. + +{% endnote %} + +You can use [quantifiers](https://en.wikipedia.org/wiki/Regular_expression#Quantification) in `PATTERN`. In regular expressions, they determine the number of repetitions of an element or subsequence in the matched pattern. Here is the list of supported quantifiers: + +|Quantifier|Description| +|-|-| +|`A+`|One or more occurrences of `A`| +|`A*`|Zero or more occurrences of `A`| +|`A?`|Zero or one occurrence of `A`| +|`B{n}`|Exactly `n` occurrences of `B`| +|`C{n, m}`|From `n` to `m` occurrences of `C`| +|`D{n,}`|At least `n` occurrences of `D`| +|`(A\|B)`|Occurrence of `A` or `B` in the data| +|`(A\|B){,m}`|From zero to `m` occurrences of `A` or `B`| + +Supported pattern search sequences: + +|Supported sequences|Syntax|Description| +|-|-|-| +|Sequence|`A B+ C+ D+`|The system searches for the exact specified sequence, the occurrence of other variables within the sequence is not allowed. The pattern search is performed in the order of the pattern variables.| +|One of|`A \| B \| C`|Variables are listed in any order with a pipe \| between them. The search is performed for any variable from the specified list.| +|Grouping|`(A \| B)+ \| C`|Variables inside round brackets are considered a single group. In this case, quantifiers apply to the entire group.| +|Exclusion from result|`{- A B+ C -}`|Rows found by the pattern in parentheses will be excluded from the result in [`ALL ROWS PER MATCH`](#rows_per_match) mode| + +#### **Example** {#pattern-example} + +```sql +PATTERN (B1 E* B2+ B3) +DEFINE + B1 as B1.button = 1, + B2 as B2.button = 2, + B3 as B3.button = 3 +``` + +The `DEFINE` section describes the `B1`, `B2`, and `B3` variables, while it does not describe `E`. Such notation allows interpreting `E` as any event, so the following pattern will be searched: one `button 1` click, one or more `button 2` clicks, and one `button 3` click. Meanwhile, between a click of `button 1` and `button 2`, any number of any other events may occur. + +### MEASURES {#measures} + +```sql +MEASURES <expression_1> AS <column_name_1> [ ... , <expression_N> AS <column_name_N> ] +``` + +`MEASURES` describes the set of returned columns when a pattern is found. A set of returned columns should be represented by an SQL expression with the aggregate functions over the variables declared in the [`DEFINE`](#define) statement. + +#### **Example** {#measures-example} + +The input data for the example are: + +|ts|button|device_id|zone_id| +|:-:|:-:|:-:|:-:| +|100|1|3|0| +|200|1|3|1| +|300|2|2|0| +|400|3|1|1| + +```sql +MEASURES + AGGREGATE_LIST(B1.zone_id * 10 + B1.device_id) AS ids, + COUNT(DISTINCT B1.zone_id) AS count_zones, + LAST(B3.ts) - FIRST(B1.ts) AS time_diff, + 42 AS meaning_of_life +PATTERN (B1+ B2 B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Result: + +|ids|count_zones|time_diff|meaning_of_life| +|:-:|:-:|:-:|:-:| +|[3,13]|2|300|42| + +The `ids` column contains the list of `zone_id * 10 + device_id` values counted among the rows matched with the `B1` variable. The `count_zones` column contains the number of unique values of the `zone_id` column among the rows matched with the `B1` variable. Column `time_diff` contains the difference between the value of column `ts` in the last row of the set of rows matched with variable `B3` and the value of column `ts` in the first row of the set of rows matched with variable `B1`. The `meaning_of_life` column contains the constant `42`. Thus, an expression in `MEASURES` may contain aggregate functions over multiple variables, but there must be only one variable within a single aggregate function. + +### ROWS PER MATCH {#rows_per_match} + +`ROWS PER MATCH` determines the number of result rows for each match found, as well as the number of columns returned. The default mode is `ONE ROW PER MATCH`. + +`ONE ROW PER MATCH` sets the `ROWS PER MATCH` mode to output one row for the match found. The structure of the returned data corresponds to the columns listed in [`PARTITION BY`](#partition_by) and [`MEASURES`](#measures). + +`ALL ROWS PER MATCH` sets the `ROWS PER MATCH` mode to output all rows of the match found except explicitly excluded by parentheses. In addition to the columns of the source table, the structure of the returned data includes the columns listed in the [`MEASURES`](#measures). + +#### **Examples** {#rows_per_match-examples} + +The input data for all examples are: + +|ts|button| +|:-:|:-:| +|100|1| +|200|2| +|300|3| + +##### **Example 1** {#rows_per_match-example1} + +```sql +MEASURES + FIRST(B1.ts) AS first_ts, + FIRST(B2.ts) AS mid_ts, + LAST(B3.ts) AS last_ts +ONE ROW PER MATCH +PATTERN (B1 {- B2 -} B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Result: + +|first_ts|mid_ts|last_ts| +|:-:|:-:|:-:| +|100|200|300| + +##### **Example 2** {#rows_per_match-example2} + +```sql +MEASURES + FIRST(B1.ts) AS first_ts, + FIRST(B2.ts) AS mid_ts, + LAST(B3.ts) AS last_ts +ALL ROWS PER MATCH +PATTERN (B1 {- B2 -} B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Result: + +|first_ts|mid_ts|last_ts|button|ts| +|:-:|:-:|:-:|:-:|:-:| +|100|200|300|1|100| +|100|200|300|3|300| + +### AFTER MATCH SKIP {#after_match_skip} + +`AFTER MATCH SKIP` determines the method of transitioning from the found match to searching for the next one. In the `AFTER MATCH SKIP TO NEXT ROW` mode, the search for the next match starts after the first row of the previous one, while in the `AFTER MATCH SKIP PAST LAST ROW` mode it starts after the last row of the previous match. The default mode is `PAST LAST ROW`. + +#### Examples {#after_match_skip-examples} + +The input data for all examples are: + +|ts|button| +|:-:|:-:| +|100|1| +|200|1| +|300|2| +|400|3| + +##### **Example 1** {#after_match_skip-example1} + +```sql +MEASURES + FIRST(B1.ts) AS first_ts, + LAST(B3.ts) AS last_ts +AFTER MATCH SKIP TO NEXT ROW +PATTERN (B1+ B2 B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Result: + +|first_ts|last_ts| +|:-:|:-:| +|100|400| +|200|400| + +##### **Example 2** {#after_match_skip-example2} + +```sql +MEASURES + FIRST(B1.ts) AS first_ts, + LAST(B3.ts) AS last_ts +AFTER MATCH SKIP PAST LAST ROW +PATTERN (B1+ B2 B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Result: + +|first_ts|last_ts| +|:-:|:-:| +|100|400| + +### ORDER BY {#order_by} + +```sql +ORDER BY <sort_key_1> [ ... , <sort_key_N> ] + +<sort_key> ::= { <column_names> | <expression> } +``` + +`ORDER BY` determines sorting of the input data. That is, before all pattern search operations are performed, the data will be pre-sorted according to the specified keys or expressions. The syntax is similar to the `ORDER BY` SQL expression. + +#### **Example** {#order_by-example} + +```sql +ORDER BY CAST(ts AS Timestamp) +``` + +### PARTITION BY {#partition_by} + +```sql +PARTITION BY <partition_1> [ ... , <partition_N> ] + +<partition> ::= { <column_names> | <expression> } +``` + +`PARTITION BY` partitions the source data into multiple non-overlapping groups, each used for an independent pattern search. If the expression is not specified, all data is processed as a single group. Records with the same values of the columns listed after `PARTITION BY` fall into the same group. + +#### **Example** {#partition_by-example} + +```sql +PARTITION BY device_id, zone_id +``` + +## Limitations {#limitations} + +Our support for the `MATCH_RECOGNIZE` expression will eventually comply with [SQL-2016](https://ru.wikipedia.org/wiki/SQL:2016); currently, however, the following limitations apply: + +- [`MEASURES`](#measures). Functions `PREV`/`NEXT` are not supported. +- [`AFTER MATCH SKIP`](#after_match_skip). Only the `AFTER MATCH SKIP TO NEXT ROW` and `AFTER MATCH SKIP PAST LAST ROW` modes are supported. +- [`PATTERN`](#pattern). Union pattern variables are not implemented. +- [`DEFINE`](#define). Aggregation functions are not supported. + +## Example of usage {#example} + +Here is a hands-on example of pattern recognizing in a data table produced by an IoT device, where pressing its buttons triggers certain events. Let's assume you need to find and process the following sequence of button clicks: `button 1`, `button 2`, and `button 3`. + +The structure of the data to transmit is as follows: + +|ts|button|device_id|zone_id| +|:-:|:-:|:-:|:-:| +|600|3|17|3| +|500|3|4|2| +|400|2|17|3| +|300|2|4|2| +|200|1|17|3| +|100|1|4|2| + +The body of the SQL query looks like this: + +```sql +PRAGMA FeatureR010="prototype"; -- pragma for enabling MATCH_RECOGNIZE + +SELECT * FROM input MATCH_RECOGNIZE ( -- Performing pattern matching from input + PARTITION BY device_id, zone_id -- Partitioning the input data into groups by columns device_id and zone_id + ORDER BY ts -- Viewing events based on the ts column data sorted ascending + MEASURES + LAST(B1.ts) AS b1, -- Going to get the latest timestamp of clicking button 1 in the query results + LAST(B3.ts) AS b3 -- Going to get the latest timestamp of clicking button 3 in the query results + ONE ROW PER MATCH -- Going to get one result row per match hit + AFTER MATCH SKIP TO NEXT ROW -- Going to move to the next row once the match is found + PATTERN (B1 B2+ B3) -- Searching for a pattern that includes one button 1 click, one or more button 2 clicks, and one button 3 click + DEFINE + B1 AS B1.button = 1, -- Defining the B1 variable as event of clicking button 1 (the button field equals 1) + B2 AS B2.button = 2, -- Defining the B2 variable as event of clicking button 2 (the button field equals 2) + B3 AS B3.button = 3 -- Defining the B3 variable as event of clicking button 3 (the button field equals 3) +); +``` + +Result: + +|b1|b3|device_id|zone_id| +|:-:|:-:|:-:|:-:| +|100|500|4|2| +|200|600|17|3| diff --git a/ydb/docs/en/core/yql/reference/syntax/select/toc_i.yaml b/ydb/docs/en/core/yql/reference/syntax/select/toc_i.yaml index 7d70ff9dccb..5ea875a67f1 100644 --- a/ydb/docs/en/core/yql/reference/syntax/select/toc_i.yaml +++ b/ydb/docs/en/core/yql/reference/syntax/select/toc_i.yaml @@ -21,3 +21,4 @@ items: - { name: LIMIT OFFSET, href: limit_offset.md } - { name: SAMPLE, href: sample.md } - { name: TABLESAMPLE, href: sample.md } +- { name: MATCH_RECOGNIZE, href: match_recognize.md } diff --git a/ydb/docs/ru/core/yql/reference/syntax/select/index.md b/ydb/docs/ru/core/yql/reference/syntax/select/index.md index 77ecdf45dcc..4dd84a3a36b 100644 --- a/ydb/docs/ru/core/yql/reference/syntax/select/index.md +++ b/ydb/docs/ru/core/yql/reference/syntax/select/index.md @@ -20,7 +20,8 @@ SELECT 2 + 2; Результат запроса `SELECT` вычисляется следующим образом: * определяется набор входных таблиц – вычисляются выражения после [FROM](../select/from.md); -* к входным таблицам применяется [SAMPLE](sample.md) / [TABLESAMPLE](sample.md) +* к входным таблицам применяется [MATCH_RECOGNIZE](match_recognize.md) +* вычисляется [SAMPLE](sample.md) / [TABLESAMPLE](sample.md) * выполняется [FLATTEN COLUMNS](../flatten.md#flatten-columns) или [FLATTEN BY](../flatten.md); алиасы, заданные во `FLATTEN BY`, становятся видны после этой точки; {% if feature_join %} * выполняются все [JOIN](../join.md); @@ -222,6 +223,7 @@ SELECT * FROM FILTER( * [LIMIT OFFSET](limit_offset.md) * [SAMPLE](sample.md) * [TABLESAMPLE](sample.md) +* [MATCH_RECOGNIZE](match_recognize.md) {% if yt %} diff --git a/ydb/docs/ru/core/yql/reference/syntax/select/match_recognize.md b/ydb/docs/ru/core/yql/reference/syntax/select/match_recognize.md new file mode 100644 index 00000000000..b682de56ab1 --- /dev/null +++ b/ydb/docs/ru/core/yql/reference/syntax/select/match_recognize.md @@ -0,0 +1,363 @@ +# MATCH_RECOGNIZE + +Выражение `MATCH_RECOGNIZE` выполняет распознавание паттерна в последовательности строк и возвращает найденные результаты. Данная функциональность важна для различных сфер деятельности, таких как выявление мошенничества, анализ ценообразования в сфере финансов и обработка данных датчиков. Эта область известна как Complex Event Processing (CEP), и распознавание паттерна является важным инструментом для этого. Пример работы `MATCH_RECOGNIZE` приведен по [ссылке](#example). + +## Алгоритм обработки данных + +Выражение `MATCH_RECOGNIZE` выполняет следующие действия: + +1. Входная таблица разбивается на непересекающиеся группы. Каждая группа состоит из набора строк входной таблицы с одинаковыми значениями в колонках, перечисленных после `PARTITION BY`. +2. Каждая группа упорядочивается в соответствии с блоком `ORDER BY`. +3. В каждой упорядоченной группе независимо выполняется распознавание паттерна из `PATTERN`. +4. Поиск паттерна в последовательности строк проходит пошагово: строки одна за другой проверяются на соответствие паттерну. Среди всех совпадений, начинающихся в первой строке, выбирается то, которое состоит из наибольшего количества строк. Если не было найдено совпадений с началом в первой строке, то поиск продолжается, начиная со следующей строки. +5. После нахождения совпадения вычисляются колонки, задающиеся выражениями в блоке `MEASURES`. +6. В зависимости от режима `ROWS PER MATCH` выводится одна или все строки для найденного совпадения. +7. Режим `AFTER MATCH SKIP` определяет, с какой строки возобновится распознавание паттерна. + +## Синтаксис {#syntax} + +```sql +MATCH_RECOGNIZE ( + [ PARTITION BY <партиция_1> [ ... , <партиция_N> ] ] + [ ORDER BY <ключ_сортировки_1> [ ... , <ключ_сортировки_N> ] ] + [ MEASURES <выражение_1> AS <имя_колонки_1> [ ... , <выражение_N> AS <имя_колонки_N> ] ] + [ ROWS PER MATCH ] + [ AFTER MATCH SKIP ] + PATTERN (<паттерн_для_поиска>) + DEFINE <переменная_1> AS <предикат_1> [ ... , <переменная_N> AS <предикат_N> ] +) +``` + +Описание элементов SQL-синтаксиса выражения `MATCH_RECOGNIZE`: + +* [`DEFINE`](#define) – блок объявления переменных, с помощью которых описывается паттерн для поиска, и условий, которым должны соответствовать строки для каждой из переменных. +* [`PATTERN`](#pattern) – [регулярное выражение](https://ru.wikipedia.org/wiki/Регулярные_выражения), описывающее искомый паттерн для поиска. +* [`MEASURES`](#measures) определяет набор колонок для возвращаемых данных. Каждая колонка задаётся SQL-выражением для ее вычисления. +* [`ROWS PER MATCH`](#rows_per_match) определяет структуру возвращаемых данных и количество строк по каждому найденному совпадению. +* [`AFTER MATCH SKIP`](#after_match_skip) определяет способ перехода к месту поиска следующего совпадения. +* [`ORDER BY`](#order_by) определяет сортировку входных данных. Поиск паттернов выполняется внутри данных, упорядоченных в соответствии со списком колонок или выражениями, перечисленными в `<ключ_сортировки_1> [ ... , <ключ_сортировки_N> ]`. +* [`PARTITION BY`](#partition_by) разделяет входную таблицу по заданным правилам в соответствии с `<партиция_1> [ ... , <партиция_N> ]`. В каждой из частей поиск паттернов производится независимо. + +### DEFINE {#define} + +```sql +DEFINE <переменная_1> AS <предикат_1> [ ... , <переменная_N> AS <предикат_N> ] +``` + +С помощью `DEFINE` объявляются переменные, на основе которых описывается искомый паттерн, задаваемый в [`PATTERN`](#pattern). Переменные – это именованные SQL-выражения, вычисляемые над входными данными. Синтаксис SQL-выражений в `DEFINE` совпадает с SQL-выражениями предиката `WHERE`. Например, выражение `button = 1` выполняет поиск строк со значением `1` в колонке `button`. В качестве условий могут выступать любые SQL-выражения, с помощью которых можно выполнять поиск, включая функции агрегации (`LAST`, `FIRST`). Например, `button > 2 AND zone_id < 12` или `LAST(button) > 10`. + +В примере ниже SQL-выражение `A.button = 1` объявлено как переменная `A`. + +```sql +DEFINE + A AS A.button = 1 +``` + +{% note info %} + +В настоящий момент в `DEFINE` не поддерживаются функции агрегации, такие как `AVG`, `MIN`, `MAX`, и функции `PREV` и `NEXT`. + +{% endnote %} + +При обработке очередной строки данных производится вычисление всех SQL-выражений, описывающих переменные в `DEFINE`. Когда SQL-выражение, описывающее соответствующую переменную из `DEFINE`, принимает значение `ИСТИНА` (`TRUE`), то такая строка получает метку с названием переменной `DEFINE` и добавляется к списку рассматриваемых на совпадение с паттернами. + +#### **Пример** {#define-example} + +При описании переменных в SQL-выражениях можно использовать ссылки на другие переменные: + +```sql +DEFINE + A AS A.button = 1 AND LAST(A.zone_id) = 12, + B AS B.button = 2 AND FIRST(A.zone_id) = 12 +``` + +Строка входных данных будет вычислена как переменная `A`, если в ней присутствует колонка `button` со значением `1`, а в последней строке из множества ранее совпавших с переменной `A` есть колонка `zone_id` со значением `12`. Строка будет вычислена как переменная `B`, если в строке данных присутствует колонка `button` со значением `2`, а в первой строке из множества ранее совпавших с переменной `A` есть колонка `zone_id` со значением `12`. + +### PATTERN {#pattern} + +```sql +PATTERN (<паттерн_для_поиска>) +``` + +Ключевое слово `PATTERN` описывает искомый паттерн для поиска в виде, вычисляющийся на основе переменных из блока `DEFINE`. Синтаксис `PATTERN` схож с синтаксисом [регулярных выражений](https://ru.wikipedia.org/wiki/Регулярные_выражения). + +{% note warning %} + +Если переменная, использованная в блоке `PATTERN`, не была предварительно описана в блоке `DEFINE`, то считается, что она всегда принимает значение `TRUE`. + +{% endnote %} + +В `PATTERN` можно использовать [квантификаторы](https://ru.wikipedia.org/wiki/Регулярные_выражения#Квантификация_(поиск_последовательностей)). Они в регулярных выражениях определяют число повторений элемента или подпоследовательности в паттерне для нахождения совпадения. Приведем перечень поддерживаемых квантификаторов: + +|Квантификатор|Описание| +|-|-| +|`A+`|Одно или несколько повторений переменной `A`| +|`A*`|Ноль или несколько повторений переменной `A`| +|`A?`|Ноль или одно повторение переменной `A`| +|`B{n}`|Ровно `n` повторений переменной `B`| +|`C{n, m}`|От `n` до `m` повторений переменной `C`| +|`D{n,}`|Не менее `n` повторений переменной `D`| +|`(A\|B)`|Появление переменной `A` или `B` в данных| +|`(A\|B){,m}`|От нуля до `m` повторений переменной `A` или `B`|| + +Поддерживаемые последовательности поиска паттернов: + +|Поддерживаемые последовательности|Синтаксис|Описание| +|-|-|-| +|Последовательность|`A B+ C+ D+`|Производится поиск точной указанной последовательности, не допускается появление других переменных внутри последовательности. Поиск паттерна производится в порядке указания переменных паттерна.| +|Один из|`A \| B \| C`|Переменные перечисляются в любом порядке с указанием символа \| между ними. Производится поиск любой переменной из указанного списка.| +|Группировка|`(A \| B)+ \| C`|Переменные внутри круглых скобок считаются единой группой. В этом случае квантификаторы применяются ко всей группе сразу.| +|Исключение из результата|`{- A B+ C -}`|При выборе режима [ALL ROWS PER MATCH](#rows_per_match) строки, найденные по паттерну в скобках, будут исключены из результата | + +#### **Пример** {#pattern-example} + +```sql +PATTERN (B1 E* B2+ B3) +DEFINE + B1 as B1.button = 1, + B2 as B2.button = 2, + B3 as B3.button = 3 +``` + +В блоке `DEFINE` описаны переменные `B1`, `B2`, `B3`. Переменная `E` в блоке `DEFINE` не описана. Такая запись позволяет интерпретировать `E` как любое событие, поэтому будет искаться следующий паттерн: одно нажатие `кнопки 1`, одно или более нажатий `кнопки 2` и одно нажатие `кнопки 3`. При этом между нажатием `кнопки 1` и `кнопки 2` может быть произвольное число любых других событий. + +### MEASURES {#measures} + +```sql +MEASURES <выражение_1> AS <имя_колонки_1> [ ... , <выражение_N> AS <имя_колонки_N> ] +``` + +`MEASURES` описывает набор возвращаемых колонок при нахождении паттерна. Набор возвращаемых колонок должен быть представлен SQL-выражением c агрегирующими функциями над переменными, объявленными в конструкции [`DEFINE`](#define). + +#### **Пример** {#measures-example} + +Входными данными для примера являются: + +|ts|button|device_id|zone_id| +|:-:|:-:|:-:|:-:| +|100|1|3|0| +|200|1|3|1| +|300|2|2|0| +|400|3|1|1| + +```sql +MEASURES + AGGREGATE_LIST(B1.zone_id * 10 + B1.device_id) AS ids, + COUNT(DISTINCT B1.zone_id) AS count_zones, + LAST(B3.ts) - FIRST(B1.ts) AS time_diff, + 42 AS meaning_of_life +PATTERN (B1+ B2 B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Результат: + +|ids|count_zones|time_diff|meaning_of_life| +|:-:|:-:|:-:|:-:| +|[3,13]|2|300|42| + +Колонка `ids` содержит список значений `zone_id * 10 + device_id`, посчитанных среди совпавших с переменной `B1` строк. Колонка `count_zones` содержит количество уникальных значений колонки `zone_id` среди совпавших с переменной `B1` строк. Колонка `time_diff` содержит разницу между значением колонки `ts` в последней строке из множества совпавших с переменной `B3` и значением колонки `ts` в первой строке из множества совпавших с переменной `B1`. Колонка `meaning_of_life` содержит константу `42`. Таким образом, выражение в `MEASURES` может содержать агрегирующие функции над несколькими переменными, но внутри одной агрегирующей функции должна быть только одна переменная. + +### ROWS PER MATCH {#rows_per_match} + +`ROWS PER MATCH` определяет количество строк результата для каждого найденного совпадения, а также множество возвращаемых колонок. Режим по умолчанию - `ONE ROW PER MATCH`. + +`ONE ROW PER MATCH` устанавливает режим работы `ROWS PER MATCH` на вывод одной строки для найденного совпадения. Структура возвращаемых данных соответствует колонкам, перечисленным в [`PARTITION BY`](#partition_by) и [`MEASURES`](#measures). + +`ALL ROWS PER MATCH` устанавливает режим работы `ROWS PER MATCH` на вывод всех строк для найденного совпадения, кроме явно исключенных скобками. Помимо колонок исходной таблицы структура возвращаемых данных включает в себя колонки, перечисленные в [`MEASURES`](#measures). + +#### **Примеры** {#rows_per_match-examples} + +Входными данными для всех примеров являются: + +|ts|button| +|:-:|:-:| +|100|1| +|200|2| +|300|3| + +##### **Пример 1** {#rows_per_match-example1} + +```sql +MEASURES + FIRST(B1.ts) AS first_ts, + FIRST(B2.ts) AS mid_ts, + LAST(B3.ts) AS last_ts +ONE ROW PER MATCH +PATTERN (B1 {- B2 -} B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Результат: + +|first_ts|mid_ts|last_ts| +|:-:|:-:|:-:| +|100|200|300| + +##### **Пример 2** {#rows_per_match-example2} + +```sql +MEASURES + FIRST(B1.ts) AS first_ts, + FIRST(B2.ts) AS mid_ts, + LAST(B3.ts) AS last_ts +ALL ROWS PER MATCH +PATTERN (B1 {- B2 -} B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Результат: + +|first_ts|mid_ts|last_ts|button|ts| +|:-:|:-:|:-:|:-:|:-:| +|100|200|300|1|100| +|100|200|300|3|300| + +### AFTER MATCH SKIP {#after_match_skip} + +`AFTER MATCH SKIP` определяет способ перехода от найденного совпадения к поиску следующего. В режиме `AFTER MATCH SKIP TO NEXT ROW` поиск следующего совпадения начинается после первой строки предыдущего, в режиме `AFTER MATCH SKIP PAST LAST ROW` - после последней строки предыдущего совпадения. Режим по умолчанию - `PAST LAST ROW`. + +#### **Примеры** {#after_match_skip-examples} + +Входными данными для всех примеров являются: + +|ts|button| +|:-:|:-:| +|100|1| +|200|1| +|300|2| +|400|3| + +##### **Пример 1** {#after_match_skip-example1} + +```sql +MEASURES + FIRST(B1.ts) AS first_ts, + LAST(B3.ts) AS last_ts +AFTER MATCH SKIP TO NEXT ROW +PATTERN (B1+ B2 B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Результат: + +|first_ts|last_ts| +|:-:|:-:| +|100|400| +|200|400| + +##### **Пример 2** {#after_match_skip-example2} + +```sql +MEASURES + FIRST(B1.ts) AS first_ts, + LAST(B3.ts) AS last_ts +AFTER MATCH SKIP PAST LAST ROW +PATTERN (B1+ B2 B3) +DEFINE + B1 AS B1.button = 1, + B2 AS B2.button = 2, + B3 AS B3.button = 3 +``` + +Результат: + +|first_ts|last_ts| +|:-:|:-:| +|100|400| + +### ORDER BY {#order_by} + +```sql +ORDER BY <ключ_сортировки_1> [ ... , <ключ_сортировки_N> ] + +<ключ_сортировки> ::= { <имена_колонок> | <выражение> } +``` + +`ORDER BY` определяет сортировку входных данных. То есть перед выполнением всех операций поиска паттернов, данные будут предварительно отсортированы по указанным ключам или выражениям. Синтаксис аналогичен SQL-выражению `ORDER BY`. + +#### **Пример** {#order_by-example} + +```sql +ORDER BY CAST(ts AS Timestamp) +``` + +### PARTITION BY {#partition_by} + +```sql +PARTITION BY <партиция_1> [ ... , <партиция_N> ] + +<партиция> ::= { <имена_колонок> | <выражение> } +``` + +`PARTITION BY` - выражение разбивает исходные данные на несколько непересекающихся групп, в каждой из которых независимо производится поиск паттернов. Если выражение не указывается, то все данные обрабатываются в виде единой группы. В группу попадают записи с одинаковыми значениями колонок, перечисленных после `PARTITION BY`. + +#### **Пример** {#partition_by-example} + +```sql +PARTITION BY device_id, zone_id +``` + +## Ограничения {#limitations} + +Степень поддержки выражения `MATCH_RECOGNIZE` со временем будет соответствовать стандарту [SQL-2016](https://ru.wikipedia.org/wiki/SQL:2016), однако сейчас действует следующий набор ограничений: + +- [`MEASURES`](#measures). Не поддерживаются функции `PREV`/`NEXT`. +- [`AFTER MATCH SKIP`](#after_match_skip). Поддерживаются только режимы `AFTER MATCH SKIP TO NEXT ROW` и `AFTER MATCH SKIP PAST LAST ROW`. +- [`PATTERN`](#pattern). Не реализованы Union pattern variables. +- [`DEFINE`](#define). Не поддерживаются агрегатные функции. + +## Пример использования {#example} + +Разберем практический пример распознавания паттернов в таблице, созданной IoT-устройством с кнопками и событиями, вызываемыми при их активации. Нам нужно найти и обработать следующую последовательность нажатия кнопок: `кнопка 1`, `кнопка 2`, `кнопка 3`. + +Структура передаваемых данных: + +|ts|button|device_id|zone_id| +|:-:|:-:|:-:|:-:| +|600|3|17|3| +|500|3|4|2| +|400|2|17|3| +|300|2|4|2| +|200|1|17|3| +|100|1|4|2| + +Тело SQL-запроса: + +```sql +PRAGMA FeatureR010="prototype"; -- прагма для включения MATCH_RECOGNIZE + +SELECT * FROM input MATCH_RECOGNIZE ( -- выполняем поиск паттернов из таблицы input + PARTITION BY device_id, zone_id -- разбиваем данные на группы по колонкам device_id и zone_id + ORDER BY ts -- просматриваем события в порядке возрастания значения колонки ts + MEASURES + LAST(B1.ts) AS b1, -- в результатах запроса будем получать последний момент нажатия на кнопку 1 + LAST(B3.ts) AS b3 -- в результатах запроса будем получать последний момент нажатия на кнопку 3 + ONE ROW PER MATCH -- будем получать одну строку результатов на найденное совпадение + AFTER MATCH SKIP TO NEXT ROW -- после нахождения совпадения перейдем на следующую строку + PATTERN (B1 B2+ B3) -- ищем паттерн, состоящий из одного нажатия на кнопку 1, одного или нескольких нажатий на кнопку 2 и одного нажатия на кнопку 3 + DEFINE + B1 AS B1.button = 1, -- определяем переменную B1 как событие нажатия кнопки 1 (значение поля button равно 1) + B2 AS B2.button = 2, -- определяем переменную B2 как событие нажатия кнопки 2 (значение поля button равно 2) + B3 AS B3.button = 3 -- определяем переменную B3 как событие нажатия кнопки 3 (значение поля button равно 3) +); +``` + +Результат: + +|b1|b3|device_id|zone_id| +|:-:|:-:|:-:|:-:| +|100|500|4|2| +|200|600|17|3| diff --git a/ydb/docs/ru/core/yql/reference/syntax/select/toc_i.yaml b/ydb/docs/ru/core/yql/reference/syntax/select/toc_i.yaml index b5d9b42cd9c..675b5617e23 100644 --- a/ydb/docs/ru/core/yql/reference/syntax/select/toc_i.yaml +++ b/ydb/docs/ru/core/yql/reference/syntax/select/toc_i.yaml @@ -21,7 +21,4 @@ items: - { name: LIMIT OFFSET, href: limit_offset.md } - { name: SAMPLE, href: sample.md } - { name: TABLESAMPLE, href: sample.md } - - - - +- { name: MATCH_RECOGNIZE, href: match_recognize.md } |
