Oracle 12c で導入された MATCH_RECOGNIZE は、正規表現のような構文を使って行の並びに対してパターンマッチングを行う SQL 機能です。標準 SQL:2016 にも採用されており、Oracle が最も早く実装した高度な分析機能のひとつです。
通常のウィンドウ関数(ROW_NUMBER / LAG / LEAD)では複雑になる「連続した行のパターン検出」を、MATCH_RECOGNIZE を使うと直感的に記述できます。株価の上昇→頂点→下降パターン検出・ユーザーのセッション時間集計・センサーデータの異常値パターン検出などに活用できます。
この記事でわかること
- MATCH_RECOGNIZE の基本構文(PARTITION BY / ORDER BY / MEASURES / PATTERN / DEFINE)
- PATTERN 句で行のパターンを正規表現ライクに記述する方法
- DEFINE 句でパターン変数の条件を定義する方法
- MEASURES 句でマッチした行から集計値を取り出す方法
- ONE ROW PER MATCH と ALL ROWS PER MATCH の違い
- AFTER MATCH SKIP でマッチ後の再開位置を制御する方法
- 実用例: 株価パターン検出・セッション集計
MATCH_RECOGNIZE の基本構文
MATCH_RECOGNIZE の基本構文と各句の役割
SELECT *
FROM テーブル名
MATCH_RECOGNIZE (
PARTITION BY 列名 -- グループ分けの基準(省略可)
ORDER BY 列名 -- 行の順序を定義する(必須)
MEASURES -- マッチ結果から取り出す値を定義する
A.列名 AS 別名,
LAST(B.列名) AS 別名
ONE ROW PER MATCH -- マッチごとに1行を返す(デフォルト)
-- ALL ROWS PER MATCH -- マッチした全行を返す
AFTER MATCH SKIP PAST LAST ROW -- マッチ後の検索再開位置
PATTERN ( A+ B+ ) -- 行パターンを正規表現で定義する
DEFINE -- パターン変数の条件を定義する
A AS A.price <= PREV(A.price), -- A: 前の行以下
B AS B.price > PREV(B.price) -- B: 前の行より大きい
);
-- PATTERN で使える正規表現演算子
-- A : パターン変数 A が1回一致
-- A+ : A が1回以上一致
-- A* : A が0回以上一致
-- A? : A が0回または1回一致
-- A | B : A または B が一致
-- (A B) : A の後に B
-- A{n} : A が n 回一致
-- A{m,n} : A が m 回以上 n 回以下
実用例:株価の上昇→頂点→下降パターンを検出する
株価データで「価格が上昇し続けた後、頂点を迎えて下降に転じる」パターンを検出します。これは LAG 関数の組み合わせでも実装できますが、MATCH_RECOGNIZE を使うと直感的に書けます。
株価の上昇→頂点→下降パターンを検出する
-- サンプルデータの想定:
-- stock_prices (ticker, trade_date, close_price)
SELECT
ticker,
pattern_start,
pattern_end,
peak_date,
peak_price,
start_price,
end_price
FROM stock_prices
MATCH_RECOGNIZE (
PARTITION BY ticker -- 銘柄ごとにパターンを探す
ORDER BY trade_date
MEASURES
FIRST(UP.trade_date) AS pattern_start, -- 上昇開始日
LAST(DOWN.trade_date) AS pattern_end, -- 下降終了日
FIRST(DOWN.trade_date) AS peak_date, -- 最初の下降日(頂点の翌日)
LAST(UP.close_price) AS peak_price, -- 上昇最終値(頂点価格)
FIRST(UP.close_price) AS start_price, -- パターン開始価格
LAST(DOWN.close_price) AS end_price -- パターン終了価格
ONE ROW PER MATCH
AFTER MATCH SKIP TO LAST DOWN -- 最後の DOWN 行から再開する
PATTERN ( UP+ DOWN+ ) -- 1回以上の上昇後に1回以上の下降
DEFINE
UP AS UP.close_price > PREV(UP.close_price), -- 前日より高い
DOWN AS DOWN.close_price < PREV(DOWN.close_price) -- 前日より低い
)
ORDER BY ticker, pattern_start;
-- 出力例:
-- TICKER PATTERN_START PATTERN_END PEAK_DATE PEAK_PRICE START_PRICE END_PRICE
-- AAPL 2024-01-05 2024-01-15 2024-01-10 195.0 185.0 175.0
実用例:ユーザーのセッション時間を集計する
ログデータからユーザーの「ログイン→操作→ログアウト」という一連のセッションを特定して、セッション時間を集計する例です。
ユーザーセッションの開始・終了・所要時間を集計する
-- サンプルデータの想定:
-- user_events (user_id, event_time, event_type)
-- event_type: LOGIN / ACTION / LOGOUT
SELECT
user_id,
session_start,
session_end,
ROUND((session_end - session_start) * 24 * 60, 1) AS duration_minutes,
action_count
FROM user_events
MATCH_RECOGNIZE (
PARTITION BY user_id
ORDER BY event_time
MEASURES
LOGIN.event_time AS session_start, -- ログイン時刻
LAST(LOGOUT.event_time) AS session_end, -- ログアウト時刻
COUNT(ACTION.*) AS action_count -- セッション内の操作回数
ONE ROW PER MATCH
AFTER MATCH SKIP PAST LAST ROW
PATTERN ( LOGIN ACTION* LOGOUT ) -- ログイン→0回以上の操作→ログアウト
DEFINE
LOGIN AS LOGIN.event_type = 'LOGIN',
ACTION AS ACTION.event_type = 'ACTION',
LOGOUT AS LOGOUT.event_type = 'LOGOUT'
)
ORDER BY user_id, session_start;
ONE ROW PER MATCH と ALL ROWS PER MATCH の違い
ALL ROWS PER MATCH でマッチした全行を取得する
-- ONE ROW PER MATCH: マッチごとに集計値を1行で返す(デフォルト)
-- ALL ROWS PER MATCH: マッチした各行に対して結果を返す(デバッグ・詳細分析に便利)
SELECT *
FROM stock_prices
MATCH_RECOGNIZE (
PARTITION BY ticker
ORDER BY trade_date
MEASURES
MATCH_NUMBER() AS match_num, -- 何番目のマッチか
CLASSIFIER() AS var_name, -- この行がどのパターン変数か (UP/DOWN)
close_price AS price,
FIRST(UP.trade_date) AS match_start
ALL ROWS PER MATCH -- マッチした全行を個別に返す
AFTER MATCH SKIP PAST LAST ROW
PATTERN ( UP+ DOWN+ )
DEFINE
UP AS UP.close_price > PREV(UP.close_price),
DOWN AS DOWN.close_price < PREV(DOWN.close_price)
)
WHERE ticker = 'AAPL'
ORDER BY trade_date;
-- MATCH_NUMBER(): このマッチが全体の何番目のマッチかを返す
-- CLASSIFIER(): 各行がどのパターン変数(UP/DOWN)に分類されているかを返す
-- これらは MEASURES 句または ALL ROWS PER MATCH 時に使える
AFTER MATCH SKIP でマッチ後の再開位置を制御する
| オプション | 動作 | 使用場面 |
|---|---|---|
SKIP PAST LAST ROW |
マッチした最後の行の次から再開する(重複なし) | 重複しないマッチが必要なとき(デフォルト) |
SKIP TO NEXT ROW |
マッチ開始行の次から再開する(重複マッチ可) | 重複するマッチも検出したいとき |
SKIP TO FIRST 変数名 |
マッチ内の指定変数の最初の行から再開する | 特定の変数位置から再開したいとき |
SKIP TO LAST 変数名 |
マッチ内の指定変数の最後の行から再開する | 変数の末尾から次のマッチを探したいとき |
まとめ
- MATCH_RECOGNIZE:Oracle 12c 以降で使える行パターンマッチング。正規表現ライクな PATTERN 句で連続する行のパターンを検出できる
- PARTITION BY / ORDER BY:グループ分けと行の順序を定義する。ORDER BY は必須
- PATTERN 句:
A+(1回以上)/A*(0回以上)/A?(0または1回)/|(OR)を使って行のパターンを定義する - DEFINE 句:各パターン変数が満たすべき条件を SQL で記述する。
PREV()で1つ前の行を参照できる - MEASURES 句:マッチした行から集計値を取り出す。
FIRST()/LAST()でマッチ内の先頭・末尾を取得できる - ONE ROW PER MATCH:マッチごとに1行を返す。
ALL ROWS PER MATCHでマッチした全行を個別に取得できる - CLASSIFIER() / MATCH_NUMBER():ALL ROWS PER MATCH 時に各行の分類とマッチ番号を取得できる
時系列データの分析には分析関数(ウィンドウ関数)との組み合わせも有効です。Oracle 分析関数完全ガイドとOracle 階層問合せ完全ガイドも参照してください。