ウィンドウ関数
ウィンドウ関数を使用すると、現在の行に関連する一連の行に対して計算を行うことができます。実行可能な計算の一部は、集約関数を使用した場合と似ていますが、ウィンドウ関数は行を単一の出力にグループ化するのではなく、個々の行をそのまま返します。
標準ウィンドウ関数
ClickHouseは、ウィンドウとウィンドウ関数を定義するための標準的な文法をサポートしています。以下の表は、現在サポートされている機能を示しています。
| 機能 | サポートされているか? |
|---|---|
ad hocウィンドウ指定(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() |
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- 集計関数の計算中にグループ内の行をどのように順序付けるかを定義します。ROWS or RANGE- フレームの範囲を定義し、集約関数はフレーム内で計算されます。WINDOW- 複数の式が同じウィンドウ定義を使用できるようにします。
関数
これらの関数はウィンドウ関数としてのみ使用できます。
row_number()- 現在の行をそのパーティション内で1から始まる番号を付けます。first_value(x)- 整列されたフレーム内で評価された最初の値を返します。last_value(x)- 整列されたフレーム内で評価された最後の値を返します。nth_value(x, offset)- 整列されたフレーム内でn行目(オフセット)に対して評価された最初の非NULL値を返します。rank()- 隙間のある状態でそのパーティション内の現在の行にランクを付けます。dense_rank()- 隙間のない状態でそのパーティション内の現在の行にランクを付けます。lagInFrame(x)- 整列されたフレーム内で現在の行の前にある指定された物理的オフセット行に対して評価された値を返します。leadInFrame(x)- 整列されたフレーム内で現在の行の後にあるオフセット行に対して評価された値を返します。
例
ウィンドウ関数がどのように使われるかをいくつかの例で見てみましょう。
行番号付け
集約関数
各プレイヤーの給与をチームの平均と比較します。
各プレイヤーの給与をチームの最大値と比較します。
カラムによるパーティショニング
フレームの境界
実世界の例
以下の例は一般的な実世界の問題を解決します。
部門ごとの最大/総給与
累積和
移動平均 / スライディング平均(3行ごと)
移動平均 / スライディング平均(10秒ごと)
移動平均 / スライディング平均(10日ごと)
温度は秒単位の精度で保存されていますが、RangeとORDER BY toDate(ts)を使用して10単位のサイズのフレームを形成し、toDate(ts)により単位が日になります。
参考文献
GitHub Issues
ウィンドウ関数の初期サポートのロードマップはこちらの問題にあります。
ウィンドウ関数に関連するすべてのGitHubの問題は、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