ウィンドウ関数
背景
ウィンドウ関数は、特別なクラスの組み込み関数です。集計関数と同様に、複数の入力行に対して計算を行い、単一のデータ値を取得します。違いは、ウィンドウ関数が特定のウィンドウ内で入力データを処理することであり、「group by」メソッドを使用するのではありません。各ウィンドウ内のデータは、over()句を使用してソートおよびグループ化できます。ウィンドウ関数は、各行に対して個別の値を計算するため、グループごとに1つの値を計算するのではありません。この柔軟性により、ユーザーはselect句に追加の列を追加し、結果セットをさらにフィルタリングすることができます。ウィンドウ関数は、selectリスト と句の最外部の位置にのみ表示されることができます。クエリの最後に効果を発揮し、つまり、join、where、およびgroup by操作が実行された後に適用されます。ウィンドウ関数は、トレンドの分析、外れ値の計算、大規模データのバケッティング分析にしばしば使用されます。
使用法
構文
function(args) OVER([partition_by_clause] [order_by_clause] [order_by_clause window_clause])
partition_by_clause ::= PARTITION BY expr [, expr ...]
order_by_clause ::= ORDER BY expr [ASC | DESC] [, expr [ASC | DESC] ...]
PARTITION BY句
Partition By句はGroup Byに似ています。指定された1つ以上の列で入力行をグループ化します。同じ値を持つ行は一緒にグループ化されます。
ORDER BY句
Order By句は基本的に外部のOrder Byと同じです。入力行の順序を定義します。Partition Byが指定されている場合、Order Byは各Partitionグループ内の順序を定義します。唯一の違いは、OVER句のOrder By n(nは正の整数)が操作なしに等しいのに対し、外部のOrder Byのnはn番目の列でのソートを示すことです。
例:
この例では、eventsテーブルのdate_and_time列でソートされた1, 2, 3などの値を持つid列をselectリストに追加します。
SELECT row_number() OVER (ORDER BY date_and_time) AS id,
c1, c2, c3, c4
FROM events;
ウィンドウ句
ウィンドウ句は、操作のための行の範囲を指定するために使用されます(現在の行に基づく前後の行)。次の構文をサポートしています: AVG(), COUNT(), FIRST_VALUE(), LAST_VALUE(), および SUM()。MAX() および MIN() の場合、ウィンドウ句は UNBOUNDED PRECEDING までの開始を指定できます。
構文:
ROWS BETWEEN [ { m | UNBOUNDED } PRECEDING | CURRENT ROW] [ AND [CURRENT ROW | { UNBOUNDED | n } FOLLOWING] ]
ウィンドウ関数のサンプルテーブル
このセクションでは、サンプルテーブルscoresを作成します。このテーブルを使用して、以下の多くのウィンドウ関数をテストできます。
CREATE TABLE `scores` (
`id` int(11) NULL,
`name` varchar(11) NULL,
`subject` varchar(11) NULL,
`score` int(11) NULL
)
DISTRIBUTED BY HASH(`score`) BUCKETS 10;
INSERT INTO `scores` VALUES
(1, "lily", "math", NULL),
(1, "lily", "english", 100),
(1, "lily", "physics", 60),
(2, "tom", "math", 80),
(2, "tom", "english", 98),
(2, "tom", "physics", NULL),
(3, "jack", "math", 95),
(3, "jack", "english", NULL),
(3, "jack", "physics", 99),
(4, "amy", "math", 80),
(4, "amy", "english", 92),
(4, "amy", "physics", 99),
(5, "mike", "math", 70),
(5, "mike", "english", 85),
(5, "mike", "physics", 85),
(6, "amber", "math", 92),
(6, "amber", NULL, 90),
(6, "amber", "physics", 100);
関数の例
このセクションでは、StarRocksでサポートされているウィンドウ関数について説明します。
AVG()
指定されたウィンドウ内のフィールドの平均値を計算します。この関数はNULL値を無視します。
構文:
AVG([DISTINCT] expr) [OVER (*analytic_clause*)]
DISTINCTはStarRocks v4.0からサポートされています。指定すると、AVG()はウィンドウ内の一意の値の平均のみを計算します。
ウィンドウフレーム制限: AVG(DISTINCT)をウィンドウ関数として使用する場合、RANGEフレームのみがサポートされます。ROWSフレームはサポートされていません。
例:
次の例では、株式データを例として使用します。
CREATE TABLE stock_ticker (
stock_symbol STRING,
closing_price DECIMAL(8,2),
closing_date DATETIME
)
DUPLICATE KEY(stock_symbol)
COMMENT "OLAP"
DISTRIBUTED BY HASH(closing_date);
INSERT INTO stock_ticker VALUES
("JDR", 12.86, "2014-10-02 00:00:00"),
("JDR", 12.89, "2014-10-03 00:00:00"),
("JDR", 12.94, "2014-10-04 00:00:00"),
("JDR", 12.55, "2014-10-05 00:00:00"),
("JDR", 14.03, "2014-10-06 00:00:00"),
("JDR", 14.75, "2014-10-07 00:00:00"),
("JDR", 13.98, "2014-10-08 00:00:00")
;
現在の行とその前後の行の平均終値を計算します。
select stock_symbol, closing_date, closing_price,
avg(closing_price)
over (partition by stock_symbol
order by closing_date
rows between 1 preceding and 1 following
) as moving_average
from stock_ticker;
出力:
+--------------+---------------------+---------------+----------------+
| stock_symbol | closing_date | closing_price | moving_average |
+--------------+---------------------+---------------+----------------+
| JDR | 2014-10-02 00:00:00 | 12.86 | 12.87500000 |
| JDR | 2014-10-03 00:00:00 | 12.89 | 12.89666667 |
| JDR | 2014-10-04 00:00:00 | 12.94 | 12.79333333 |
| JDR | 2014-10-05 00:00:00 | 12.55 | 13.17333333 |
| JDR | 2014-10-06 00:00:00 | 14.03 | 13.77666667 |
| JDR | 2014-10-07 00:00:00 | 14.75 | 14.25333333 |
| JDR | 2014-10-08 00:00:00 | 13.98 | 14.36500000 |
+--------------+---------------------+---------------+----------------+
例えば、最初の行の12.87500000は、"2014-10-02"の終値(12.86)、その前日"2014-10-01"(null)、およびその翌日"2014-10-03"(12.89)の平均値です。
例2: AVG(DISTINCT)で全体ウィンドウを使用
すべての行で一意のスコアの平均を計算します:
SELECT id, subject, score,
AVG(DISTINCT score) OVER () AS distinct_avg
FROM test_scores;
出力:
+----+---------+-------+-------------+
| id | subject | score | distinct_avg|
+----+---------+-------+-------------+
| 1 | math | 80 | 85.00 |
| 2 | math | 85 | 85.00 |
| 3 | math | 80 | 85.00 |
| 4 | english | 90 | 85.00 |
| 5 | english | 85 | 85.00 |
| 6 | english | 90 | 85.00 |
+----+---------+-------+-------------+
一意の平均は85.00です((80 + 85 + 90) / 3)。
例3: AVG(DISTINCT)でRANGEフレーム付きウィンドウを使用
RANGEフレームを使用して、各科目パーティション内で一意のスコアの平均を計算します:
SELECT id, subject, score,
AVG(DISTINCT score) OVER (
PARTITION BY subject
ORDER BY score
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS distinct_avg
FROM test_scores;
出力:
+----+---------+-------+-------------+
| id | subject | score | distinct_avg|
+----+---------+-------+-------------+
| 1 | math | 80 | 80.00 |
| 3 | math | 80 | 80.00 |
| 2 | math | 85 | 82.50 |
| 5 | english | 85 | 85.00 |
| 4 | english | 90 | 87.50 |
| 6 | english | 90 | 87.50 |
+----+---------+-------+-------------+
各行について、関数はパーティションの開始から現在行のスコア値(現在行を含む)までの一意のスコアの平均を計算します。
COUNT()
指定されたウィンドウ内で条件を満たす行の総数を計算します。
構文:
COUNT([DISTINCT] expr) [OVER (analytic_clause)]
DISTINCTはStarRocks v4.0からサポートされています。指定すると、COUNT()はウィンドウ内の一意の値のみをカウントします。
ウィンドウフレーム制限: COUNT(DISTINCT)をウィンドウ関数として使用する場合、RANGEフレームのみがサポートされます。ROWSフレームはサポートされていません。例:
-- サポート: RANGEフレーム
count(distinct col) OVER (PARTITION BY x ORDER BY y RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
-- サポートされていません: ROWSフレーム(エラーが発生します)
count(distinct col) OVER (PARTITION BY x ORDER BY y ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
例:
mathパーティション内で、現在の行から最初の行までのmathスコアが90を超える出現回数をカウントします。この例では、サンプルテーブル scoresのデータを使用します。
select *,
count(score)
over (
partition by subject
order by score
rows between unbounded preceding and current row
) as 'score_count'
from scores where subject in ('math') and score > 90;
+------+-------+---------+-------+-------------+
| id | name | subject | score | score_count |
+------+-------+---------+-------+-------------+
| 6 | amber | math | 92 | 1 |
| 3 | jack | math | 95 | 2 |
+------+-------+---------+-------+-------------+
例2: COUNT(DISTINCT)で全体ウィンドウを使用
すべての行で一意のスコアをカウントします:
CREATE TABLE test_scores (
id INT,
subject VARCHAR(20),
score INT
) DISTRIBUTED BY HASH(id);
INSERT INTO test_scores VALUES
(1, 'math', 80),
(2, 'math', 85),
(3, 'math', 80),
(4, 'english', 90),
(5, 'english', 85),
(6, 'english', 90);
SELECT id, subject, score,
COUNT(DISTINCT score) OVER () AS distinct_count
FROM test_scores;
出力:
+----+---------+-------+---------------+
| id | subject | score | distinct_count|
+----+---------+-------+---------------+
| 1 | math | 80 | 4 |
| 2 | math | 85 | 4 |
| 3 | math | 80 | 4 |
| 4 | english | 90 | 4 |
| 5 | english | 85 | 4 |
| 6 | english | 90 | 4 |
+----+---------+-------+---------------+
一意のカウントは4です(値: 80、85、90、およびNULLがある場合)。
例3: COUNT(DISTINCT)でRANGEフレーム付きウィンドウを使用
RANGEフレームを使用して、各科目パーティション内で一意のスコアをカウントします:
SELECT id, subject, score,
COUNT(DISTINCT score) OVER (
PARTITION BY subject
ORDER BY score
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS distinct_count
FROM test_scores;
出力:
+----+---------+-------+---------------+
| id | subject | score | distinct_count|
+----+---------+-------+---------------+
| 1 | math | 80 | 1 |
| 3 | math | 80 | 1 |
| 2 | math | 85 | 2 |
| 5 | english | 85 | 1 |
| 4 | english | 90 | 2 |
| 6 | english | 90 | 2 |
+----+---------+-------+---------------+
各行について、関数はパーティションの開始から現在行のスコア値(現在行を含む)までの一意のスコアをカウントします。
CUME_DIST()
CUME_DIST()関数は、パーティションまたはウィンドウ内での値の累積分布を計算し、その相対的な位置をパーティション内のパーセンテージとして示します。グループ内の最高値または最低値の分布を計算するために使用されます。
- データが昇順にソートされている場合、この関数は現在の行の値以下の値のパーセンテージを計算します。
- データが降順にソートされている場合、この関数は現在の行の値以上の値のパーセンテージを計算します。
累積分布は0から1の範囲にあります。パーセンタイル計算やデータ分布分析に役立ちます。
この関数はv3.2からサポートされています。
構文:
CUME_DIST() OVER (partition_by_clause order_by_clause)
partition_by_clause: 任意。指定しない場合、結果セット全体が単一のパーティションとして処理されます。order_by_clause: この関数は、パーティション行を希望の順序にソートするためにORDER BYと一緒に使用する必要があります。
CUME_DIST()はNULL値を含み、これらを最低値として扱います。
例:
次の例では、各subjectグループ内の各スコアの累積分布を示します。この例では、サンプルテーブル scoresのデータを使用します。
SELECT *,
cume_dist()
OVER (
PARTITION BY subject
ORDER BY score
) AS cume_dist
FROM scores;
+------+-------+---------+-------+---------------------+
| id | name | subject | score | cume_dist |
+------+-------+---------+-------+---------------------+
| 6 | amber | NULL | 90 | 1 |
| 3 | jack | english | NULL | 0.2 |
| 5 | mike | english | 85 | 0.4 |
| 4 | amy | english | 92 | 0.6 |
| 2 | tom | english | 98 | 0.8 |
| 1 | lily | english | 100 | 1 |
| 1 | lily | math | NULL | 0.16666666666666666 |
| 5 | mike | math | 70 | 0.3333333333333333 |
| 2 | tom | math | 80 | 0.6666666666666666 |
| 4 | amy | math | 80 | 0.6666666666666666 |
| 6 | amber | math | 92 | 0.8333333333333334 |
| 3 | jack | math | 95 | 1 |
| 2 | tom | physics | NULL | 0.16666666666666666 |
| 1 | lily | physics | 60 | 0.3333333333333333 |
| 5 | mike | physics | 85 | 0.5 |
| 4 | amy | physics | 99 | 0.8333333333333334 |
| 3 | jack | physics | 99 | 0.8333333333333334 |
| 6 | amber | physics | 100 | 1 |
+------+-------+---------+-------+---------------------+
- 最初の行の
cume_distについて、NULLグループには1行しかなく、この行自体のみが「現在の行以下」の条件を満たしています。累積分布は1です。 - 2行目の
cume_distについて、englishグループには5行あり、この行自体(NULL)のみが「現在の行以下」の条件を満たしています。累積分布は0.2です。 - 3行目の
cume_distについて、englishグループには5行あり、2行(85とNULL)が「現在の行以下」の条件を満たしています。累積分布は0.4です。
DENSE_RANK()
DENSE_RANK()関数はランキングを表現するために使用されます。RANK()とは異なり、DENSE_RANK()は空白のない番号を持ちます。例えば、1が2つある場合、DENSE_RANK()の3番目の番号は2のままですが、RANK()の3番目の番号は3になります。
構文:
DENSE_RANK() OVER(partition_by_clause order_by_clause)
例:
次の例では、mathスコアのランキング(降順)を示します。この例では、サンプルテーブル scoresのデータを使用します。
select *,
dense_rank()
over (
partition by subject
order by score desc
) as `rank`
from scores where subject in ('math');
+------+-------+---------+-------+------+
| id | name | subject | score | rank |
+------+-------+---------+-------+------+
| 3 | jack | math | 95 | 1 |
| 6 | amber | math | 92 | 2 |
| 2 | tom | math | 80 | 3 |
| 4 | amy | math | 80 | 3 |
| 5 | mike | math | 70 | 4 |
| 1 | lily | math | NULL | 5 |
+------+-------+---------+-------+------+
結果データには、スコアが80の行が2つあります。これらはすべて3位です。次のスコア70のランクは4です。これにより、DENSE_RANK()が空白のない番号を持つことが示されています。
FIRST_VALUE()
FIRST_VALUE()はウィンドウ範囲の最初の値を返します。
構文:
FIRST_VALUE(expr [IGNORE NULLS]) OVER(partition_by_clause order_by_clause [window_clause])
IGNORE NULLSはv2.5.0からサポートされています。これは、exprのNULL値を計算から除外するかどうかを決定するために使用されます。デフォルトでは、NULL値が含まれており、フィルタリングされた結果の最初の値がNULLの場合、NULLが返されます。IGNORE NULLSを指定すると、フィルタリングされた結果の最初の非NULL値が返されます。すべての値がNULLの場合、IGNORE NULLSを指定してもNULLが返されます。
ARRAYタイプはStarRocks v3.5からサポートされています。FIRST_VALUE()でARRAY列を使用して、ウィンドウ内の最初の配列値を取得できます。
例:
各グループ(降順)で、subjectでグループ化された各メンバーの最初のscore値を返します。この例では、サンプルテーブル scoresのデータを使用します。
select *,
first_value(score IGNORE NULLS)
over (
partition by subject
order by score desc
) as first
from scores;
+------+-------+---------+-------+-------+
| id | name | subject | score | first |
+------+-------+---------+-------+-------+
| 1 | lily | english | 100 | 100 |
| 2 | tom | english | 98 | 100 |
| 4 | amy | english | 92 | 100 |
| 5 | mike | english | 85 | 100 |
| 3 | jack | english | NULL | 100 |
| 6 | amber | physics | 100 | 100 |
| 3 | jack | physics | 99 | 100 |
| 4 | amy | physics | 99 | 100 |
| 5 | mike | physics | 85 | 100 |
| 1 | lily | physics | 60 | 100 |
| 2 | tom | physics | NULL | 100 |
| 6 | amber | NULL | 90 | 90 |
| 3 | jack | math | 95 | 95 |
| 6 | amber | math | 92 | 95 |
| 2 | tom | math | 80 | 95 |
| 4 | amy | math | 80 | 95 |
| 5 | mike | math | 70 | 95 |
| 1 | lily | math | NULL | 95 |
+------+-------+---------+-------+-------+
例2: FIRST_VALUE()でARRAYタイプを使用
ARRAY列を持つテーブルを作成しま す:
CREATE TABLE test_array_value (
col_1 INT,
arr1 ARRAY<INT>
) DISTRIBUTED BY HASH(col_1);
INSERT INTO test_array_value (col_1, arr1) VALUES
(1, [1, 11]),
(2, [2, 22]),
(3, [3, 33]),
(4, NULL),
(5, [5, 55]);
FIRST_VALUE()でARRAYタイプのデータをクエリします:
SELECT col_1, arr1,
FIRST_VALUE(arr1) OVER (ORDER BY col_1) AS first_array
FROM test_array_value;
出力:
+-------+--------+------------+
| col_1 | arr1 | first_array|
+-------+--------+------------+
| 1 | [1,11] | [1,11] |
| 2 | [2,22] | [1,11] |
| 3 | [3,33] | [1,11] |
| 4 | NULL | [1,11] |
| 5 | [5,55] | [1,11] |
+-------+--------+------------+
ウィンドウ内の最初の配列値[1,11]がすべての行に返されます。