ウィンドウ関数
ウィンドウ関数を使用すると、現在の行と関連する行の集合を対象に計算を実行できます。 実行できる計算の一部は集約関数で行えるものと似ていますが、ウィンドウ関数では行が 1 つの結果行にグループ化されないため、各行は個別の行として返されます。
標準ウィンドウ関数
ClickHouse は、ウィンドウおよびウィンドウ関数を定義するための標準的な文法をサポートしています。以下の表は、各機能が現在サポートされているかどうかを示します。
| 機能 | サポート状況 |
|---|---|
アドホックなウィンドウ指定(count(*) over (partition by id order by time desc)) | ✅ |
ウィンドウ関数を含む式(例: (count(*) over ()) / 2) | ✅ |
WINDOW 句(select ... from table window w as (partition by id)) | ✅ |
ROWS フレーム | ✅ |
RANGE フレーム | ✅(デフォルト) |
DateTime 用 RANGE OFFSET フレームに対する INTERVAL 構文 | ❌(代わりに秒数を指定してください。RANGE は任意の数値型で動作します) |
GROUPS フレーム | ❌ |
フレームに対する集約関数の計算(sum(value) over (order by time)) | ✅(すべての集約関数がサポートされています) |
rank(), dense_rank(), row_number() | ✅ 別名: denseRank() |
percent_rank() | ✅ パーティション内での値の相対的な順位を効率的に計算します。この関数は、ifNull((rank() OVER(PARTITION BY x ORDER BY y) - 1) / nullif(count(1) OVER(PARTITION BY x) - 1, 0), 0) のような、より冗長で計算コストの高い手動の SQL 計算を事実上置き換えます。別名: percentRank() |
cume_dist() | ✅ 値の累積分布を計算します。現在の行の値以下の値を持つ行の割合(パーセンテージ)を返します。 |
lag/lead(value, offset) | ✅ 次のいずれかの回避策も使用できます: 1) any(value) over (.... rows between <offset> preceding and <offset> preceding)、または lead の場合は following を使用します。2) ウィンドウフレームを考慮する、類似の lagInFrame/leadInFrame を使用します。lag/lead と同じ動作を得るには、rows between unbounded preceding and unbounded following を使用します。 |
| ntile(buckets) | ✅ 次のようにウィンドウを指定します: (partition by x order by y rows between unbounded preceding and unbounded following)。 |
ClickHouse固有のウィンドウ関数
以下のClickHouse固有のウィンドウ関数も提供されています:
nonNegativeDerivative(metric_column, timestamp_column[, INTERVAL X UNITS])
指定されたmetric_columnの非負の微分値をtimestamp_columnに基づいて算出します。
INTERVALは省略可能で、デフォルトはINTERVAL 1 SECONDです。
各行に対して算出される値は以下の通りです:
- 1行目:
0 - 行目:
構文
PARTITION BY- 結果セットをどのようなグループに分割するかを定義します。ORDER BY- 集約関数aggregate_functionを計算するときに、グループ内の行をどのような順序で並べるかを定義します。ROWS or RANGE- フレームの境界を定義し、集約関数aggregate_functionはそのフレーム内で計算されます。WINDOW- 複数の式で同じウィンドウ定義を共通して利用できるようにします。
関数
これらの関数は、ウィンドウ関数としてのみ使用可能です。
row_number()- パーティション内で現在の行に 1 から始まる連番を付与します。first_value(x)- 順序付けられたフレーム内で最初に評価された値を返します。last_value(x)- 順序付けられたフレーム内で最後に評価された値を返します。nth_value(x, offset)- 順序付けられたフレーム内で、offset で指定された n 行目に対して評価された最初の NULL でない値を返します。rank()- パーティション内で現在の行に順位を付けます(欠番あり)。dense_rank()- パーティション内で現在の行に順位を付けます(欠番なし)。lagInFrame(x)- 順序付けられたフレーム内で、現在の行から指定された物理オフセットだけ前の行で評価された値を返します。leadInFrame(x)- 順序付けられたフレーム内で、現在の行から指定されたオフセットだけ後ろの行で評価された値を返します。
例
ウィンドウ関数をどのように利用できるか、いくつかの例を見ていきます。
行に番号を振る
集約関数
各選手の年俸を、その所属チームの平均年俸と比較します。
各選手の給与を、その選手のチーム内での最高給与と比較します。
列によるパーティション分割
フレーム境界
┌─part_key─┬─value─┬─order─┬─frame_values─┬─rn_1─┬─rn_2─┬─rn_3─┬─rn_4─┐ │ 1 │ 1 │ 1 │ [5,4,3,2,1] │ 5 │ 5 │ 5 │ 2 │ │ 1 │ 2 │ 2 │ [5,4,3,2] │ 4 │ 4 │ 4 │ 2 │ │ 1 │ 3 │ 3 │ [5,4,3] │ 3 │ 3 │ 3 │ 2 │ │ 1 │ 4 │ 4 │ [5,4] │ 2 │ 2 │ 2 │ 2 │ │ 1 │ 5 │ 5 │ [5] │ 1 │ 1 │ 1 │ 1 │ └──────────┴───────┴───────┴──────────────┴──────┴──────┴──────┴──────┘
移動平均/スライディング平均(10秒ごと)
移動平均 / スライディング平均(10日ごと)
温度データは秒精度で保存されていますが、Range と ORDER BY toDate(ts) を使用することでサイズ 10 のフレームを作成し、toDate(ts) によってその単位は日になります。
insert into sensors values('ambient_temp', '2020-01-01 00:00:00', 16),
('ambient_temp', '2020-01-01 12:00:00', 16),
('ambient_temp', '2020-01-02 11:00:00', 9),
('ambient_temp', '2020-01-02 12:00:00', 9),
('ambient_temp', '2020-02-01 10:00:00', 10),
('ambient_temp', '2020-02-01 12:00:00', 10),
('ambient_temp', '2020-02-10 12:00:00', 12),
('ambient_temp', '2020-02-10 13:00:00', 12),
('ambient_temp', '2020-02-20 12:00:01', 16),
('ambient_temp', '2020-03-01 12:00:00', 16),
('ambient_temp', '2020-03-01 12:00:00', 16),
('ambient_temp', '2020-03-01 12:00:00', 16);
参考
GitHub Issues
ウィンドウ関数の初期サポートに関するロードマップは この Issue にあります。
ウィンドウ関数に関連するすべての GitHub Issue には comp-window-functions タグが付いています。
テスト
次のテストには、現在サポートされている構文の例が含まれています。
https://github.com/ClickHouse/ClickHouse/blob/master/tests/performance/window_functions.xml
Postgres ドキュメント
https://www.postgresql.org/docs/current/sql-select.html#SQL-WINDOW
https://www.postgresql.org/docs/devel/sql-expressions.html#SYNTAX-WINDOW-FUNCTIONS
https://www.postgresql.org/docs/devel/functions-window.html
https://www.postgresql.org/docs/devel/tutorial-window.html
MySQL ドキュメント
https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html
https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html
https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html