序章
データベース ウィンドウ関数は SQL で使用される関数で、データの分析と処理を向上させるために、結果セット内のデータをグループ化および並べ替えるために使用できます。ウィンドウ関数は集計関数とは異なり、複数行のデータを 1 行に集計するのではなく、各行のデータを保持し、グループ化して並べ替えます。
一般的なウィンドウ関数には、ROW_NUMBER()、RANK()、DENSE_RANK()、NTILE()、LAG()、LEAD() などが含まれます。これらの関数は、ユーザーが結果セット内にグループ化および並べ替えられた結果を生成して、データの理解と分析を向上させるのに役立ちます。
たとえば、ROW_NUMBER() 関数を使用すると、1 つ以上のフィールドに基づいて結果セットをグループ化し、各グループ内に行番号を生成できるため、ユーザーはデータを簡単に追跡できます。LAG() 関数と LEAD() 関数を使用して結果セットの各行の前後のデータを抽出し、ユーザーが現在の行の前後のデータを表示できるようにします。
ウィンドウ関数は SQL の非常に便利なツールであり、ユーザーが結果セット内のデータをグループ化および並べ替えて、データの分析と処理を改善するのに役立ちます。
MySQL 公式ドキュメント: https://dev.mysql.com/doc/refman/8.0/en/window-functions.html
注: ウィンドウを開く機能の公式説明は MySQL8.0 以降でのみ利用可能です。
1. ウィンドウ関数と集計関数の違いは何ですか?
- データ処理範囲: 集計関数はデータ テーブルまたはデータ セット全体に対してのみ動作し、計算結果は単一の値になります。ウィンドウ関数は行ごとに動作することができ、計算結果は行ごとに表示されます。
- 計算結果: 集計関数の計算結果は 1 つだけで、通常、合計、平均値、最大値/最小値の計算などの演算を実行するために使用されます。ウィンドウ関数は複数の結果を取得できますが、クエリ結果セットの各行に追加の列が提供されます。
- 構文: 通常、集計関数は SELECT ステートメントの SELECT 句と HAVING 句で使用され、ウィンドウ関数は通常 OVER キーワードの後に使用されます。
2. ウィンドウオープン機能を正式に説明
- 翻訳
公式声明は非常に公式なものですが、それでも少し理解するのが難しいです。
3. 窓関数の分割
3.1、シリアル番号
- ROW_NUMBER() : この関数は、結果セットを 1 つ以上のフィールドごとにグループ化し、ユーザーがデータを簡単に追跡できるように、各グループ内に行番号を生成します。
- RANK() : この関数は、ユーザーがデータのサイズと順序を理解できるように、1 つ以上のフィールドに従って結果セットを並べ替え、各並べ替えでランクを生成できます。
- DENSE_RANK() : この関数は、1 つ以上のフィールドに従って結果セットを並べ替え、各並べ替えでランキングを生成できますが、スキップされる位置は RANK() 関数より 1 つ少なくなります。
3.2. 配布
- PERCENT RANK() : この関数は、データセット内の各値のパーセンテージ ランクを計算するために使用されます。
- CUME_DIST() : データセット内の各値の累積密度ランクを計算するために使用される関数。
3.3 前後
- LAG() : この関数は結果セットの各行の前のデータを抽出できるため、ユーザーは現在の行の前のデータを表示できます。
- LEAD() : この関数は結果セットの各行の後にデータを抽出できるため、ユーザーは現在の行の後のデータを表示できます。
3.4、始まりと終わり
- FIRST_VALUE() : この関数は、結果セットの順序付けされたパーティションの最初の値を返します。
- LAST_VALUE() : この関数は、結果セットの順序付けされたパーティション内の最後の値を返します。
3.5. その他
- NTILE() : この関数は、1 つ以上のフィールドに従って結果セットをグループ化し、各グループを指定された数のバケットに分散できるため、ユーザーはデータをより適切に分析してグループ化できます。
- NTH_VALUE() : この関数は、結果セットの順序付けされたパーティションの n 番目の行の値を返します。
第四に、文法の使用
4.1. 文法構造
<窗口函数> OVER ([PARTITION BY <分组列>] [ORDER BY <排序列> {ASC|DESC}] [<行窗口>|<范围窗口>] [<开始位置>|<结束位置>|<长度>])
- <window function> は、SUM、AVG、MAX、MIN、COUNT など、実行される集計関数を示します。
- <grouping columns> はグループ化される列を示します。
- <sorting columns> は並べ替えの基準となる列を示し、カンマで区切って複数の並べ替え列を指定できます。
- <row window> と <range window> は、それぞれ行レベルのウィンドウと範囲レベルのウィンドウを表します。
- <開始位置>、<終了位置>、<長さ>はウィンドウの開始位置、終了位置、長さを示します。
MySQL 8.0 では、行ウィンドウは、全体としてみなされる連続した行のセットであり、ウィンドウ関数の計算に使用できます。
行ウィンドウは次のキーワードで指定します。
- ROWS: 行ウィンドウを示します。
- BETWEEN: 行ウィンドウの開始位置と終了位置を指定するために使用されます。
- PRECEDING: 行ウィンドウの開始位置を示します。
- FOLLOWING: 行ウィンドウの終了位置を示します。
一般的に使用される行ウィンドウの指定方法は次のとおりです。
- ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: 現在の行を含む、結果セットの最初の行から現在の行までを示します。
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING: 現在の行から、現在の行を含む結果セットの最後の行までを示します。
- ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING: 現在の行を含む前後の各行を示します。
説明: 行ウィンドウは、各グループの合計、平均、カウントなどの集計演算を計算するために使用できます。また、各行のランキング、累積和などの演算を計算するためにも使用できます。
4.2. ウィンドウ関数としての通常の集計関数
- 通常の集計関数はデータ テーブルまたはデータ セット全体に対してのみ操作でき、計算結果は単一の値になります。ウィンドウ関数は行ごとに動作することができ、計算結果は行ごとに表示されます。
4.2.1、テーブル構造
DROP TABLE IF EXISTS `order_for_goods`;
CREATE TABLE `order_for_goods` (
`order_id` int(0) NOT NULL AUTO_INCREMENT,
`user_id` int(0) NULL DEFAULT NULL,
`money` decimal(10, 2) NULL DEFAULT NULL,
`quantity` int(0) NULL DEFAULT NULL,
`join_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
4.2.2、テーブルデータ
INSERT INTO order_for_goods (user_id, money, quantity, join_time )
VALUES
( 1001, 1800.90, 1, '2023-06-07'),
( 1001, 3600.89, 5, '2023-05-02'),
( 1001, 1000.10, 6, '2023-01-08'),
( 1002, 1100.90, 9, '2023-04-07'),
( 1002, 4500.99, 1, '2023-03-14'),
( 1003, 2500.10, 3, '2023-02-14'),
( 1002, 2500.90, 1, '2023-03-14'),
( 1003, 2500.90, 1, '2022-12-12'),
( 1003, 2500.90, 2, '2022-09-08'),
( 1003, 6000.90, 8, '2023-01-10');
4.2.3. ウィンドウ関数としての通常の関数
1. 声明は次のとおりです
select
*,
sum(money) over(partition by user_id order by order_id) as alias_sum,
avg(money) over(partition by user_id order by order_id) as alias_avg,
max(money) over(partition by user_id order by order_id) as alias_max,
min(money) over(partition by user_id order by order_id) as alias_min,
count(money) over(partition by user_id order by order_id) as alias_count
from order_for_goods;
- order_for_goods テーブルからすべての列を選択し、各ユーザーの各注文の合計金額、平均金額、最大金額、最小金額、およびカウントを計算しました。
- このクエリは、sum()、avg()、max()、min()、および count() 関数を使用して、各注文の合計、平均、最大、最小、およびカウントを計算します。これらの関数の後には、計算のウィンドウを指定する over() 句が続きます。この例では、ウィンドウは user_id によって分割され、order_id によってソートされます。
2. クエリ結果は、次のように選択された列と計算された別名列を返します。
4.3、シリアル番号機能
4.3.1、ROW_NUMBER()関数
1. ステートメントを実行します
select *
from (
select *,row_number() over(partition by user_id order by money desc) as alias_row_number
from order_for_goods) t
where alias_row_number<=3;
- 上記の SQL ステートメントは、ウィンドウ関数 row_number() を使用して、各パーティション内の行にシリアル番号を割り当てます。次に、外側のクエリは、これらの序数から最初の 3 つの上位行を選択します。
- 内部クエリは、order_for_goods テーブルからすべての列を選択し、row_number() 関数を使用して各パーティション内の行に序数を割り当てます。この例では、サブクエリは user_id 列でデータを分割し、money 列で降順に並べ替えます。
- 外側のクエリは、内側のクエリの結果から、序数が 3 以下の行を選択します。これは、パーティション内の上位 3 行に対応します。
2. 実行結果
3. ステートメントを実行します
select *
from (
select *,row_number() over(partition by user_id order by money desc) as alias_row_number
from order_for_goods) t
where alias_row_number<=1;
- 上記のクエリ ステートメントは前のクエリ ステートメントと似ていますが、alias_row_number<=3 が alias_row_number<=1 に変更されるため、結果はパーティション内の最上位の行のみを返します。
4. 実行結果
要約: 発散的思考で考えることができます。たとえば、各商品分野の売上上位 3 つを数えます。ウィンドウ処理を使用すると多くの問題を解決でき、保守が困難で理解できない SQL ロジックの多くを回避できます。
4.3.2、RANK()関数
1. ステートメントを実行します
select
*,
rank() over(partition by user_id order by money desc) as alias_rank
from order_for_goods;
- 上記の SQL ステートメントは、ウィンドウ関数 Rank() を使用して、各ユーザーのエイリアス ランク (alias_rank) を計算します。
- Rank() 関数は各パーティション内の連続するランクのランク値を計算するため、このステートメントは各ユーザーのエイリアス ランクを計算します。
- このステートメントでは条件が指定されていないため、order_for_goods テーブルのすべての行と列が返されることに注意してください。特定の行または列をクエリする必要がある場合は、select 句で対応する条件または列名を指定できます。
2. 実行結果
4.3.3、DENSE_RANK()関数
1. ステートメントを実行します
select
*,
dense_rank() over(partition by user_id order by money desc) as alias_dense_rank
from order_for_goods;
- 上記の SQL ステートメントは、ウィンドウ関数 Density_rank() を使用して、各ユーザーのエイリアス密ランキング (alias_dense_rank) を計算します。
- Density_rank() 関数は、各パーティション内のランクのランク値を計算し、同じランク値を持つ隣接する行については、ランク値が連続して割り当てられます。したがって、このステートメントは各ユーザーのエイリアス密ランクを計算します。
- このステートメントでは条件が指定されていないため、order_for_goods テーブルのすべての行と列が返されることに注意してください。特定の行または列をクエリする必要がある場合は、select 句で対応する条件または列名を指定できます。
2. 実行結果
4.3.4. 上記 3 つのシリアル番号機能の比較
1. ステートメントを実行します
select
*,
row_number() over(partition by user_id order by money desc) as alias_row_number,
rank() over(partition by user_id order by money desc) as alias_rank,
dense_rank() over(partition by user_id order by money desc) as alias_dense_rank
from order_for_goods;
- order_for_goods テーブルからすべての列を選択し、各注文の各ユーザーの合計金額を計算し、各注文の各ユーザーの序数、ランク、および密ランクを計算しました。
- このクエリは、row_number()、rank()、および Density_rank() 関数を使用して、各パーティション内の行の序数、ランク、および密ランクを計算します。これらの関数の後には、計算のウィンドウを指定する over() 句が続きます。この例では、ウィンドウは user_id によってパーティション化され、money 列によって降順に並べ替えられます。
2. 実行結果
4.4. 分配機能
4.4.1、PERCENT RANK () 関数
1. ステートメントを実行します
select
*,
percent_rank() over(partition by user_id order by money desc) as alias_percent_rank
from order_for_goods;
- order_for_goods テーブルからすべての列を選択し、各注文の各ユーザーの合計金額を計算し、各注文の各ユーザーのパーセンテージ ランクを計算しました。
- このクエリは、percent_rank() 関数を使用して、各パーティション内の行のパーセンタイル ランクを計算します。この関数の後には、計算のウィンドウを指定する over() 句が続きます。この例では、ウィンドウは user_id によってパーティション化され、money 列によって降順に並べ替えられます。
2. 実行結果
4.4.2、CUME_DIST()関数
1. ステートメントを実行します
select
*,
cume_dist() over(partition by user_id order by money desc) as alias_percent_rank
from order_for_goods;
- order_for_goods テーブルからすべての列を選択し、各注文の各ユーザーの合計金額を計算し、各注文の各ユーザーの累積パーセンテージを計算しました。
- このクエリは、cume_dist() 関数を使用して、各パーティション内の行の累積パーセンテージを計算します。この関数の後には、計算のウィンドウを指定する over() 句が続きます。この例では、ウィンドウは user_id によってパーティション化され、money 列によって降順に並べ替えられます。
2. 実行結果
4.5. 表と裏の機能
4.5.1. LAG()関数
1. 文法的説明
- LAG()関数は、時系列を指定した期間だけ進めるために使用する関数です。
LAG(expression, offset, default_value)
- 式: 値を取得する列
- offset: 前方の最初の数行の値
- default_value: 値がない場合は、デフォルト値を設定できます。
2. ステートメントを実行します
select
*,
lag(join_time, 1, 0) over(partition by user_id order by join_time desc) as alias_lag
from order_for_goods;
3. 実行結果
4.5.2. LEAD()関数
1. 文法的説明
- LEAD()関数は、時系列を指定した期間だけ遡る関数です。
LAG(expression, offset, default_value)
- 式: 値を取得する列
- offset: 後方の行数の値
- default_value: 値がない場合は、デフォルト値を設定できます。
2. ステートメントを実行します
select
*,
lead(join_time, 1, 0) over(partition by user_id order by join_time desc) as alias_lead
from order_for_goods;
3. 実行結果
4.6. クロージング機能
4.6.1、FIRST_VALUE()関数
1. 文法的説明
- FIRST_VALUE: ウィンドウの最初の行の値を取得します。
FIRST_VALUE(expression)
- 式: 最初の行の値を取得する列または計算結果を指定する式。
2. 実行構文
select
*,
first_value(money) over(partition by user_id order by join_time desc) as alias_first_value
from order_for_goods;
- ユーザーが指定された時間範囲のデータを持っていない場合、LAST_VALUE() 関数はデフォルト値の NULL を返すことに注意してください。
3. 実行結果
4.6.2、LAST_VALUE()関数
1. 文法的説明
- LAST_VALUE: ウィンドウの最後の行の値を取得します。
LAST_VALUE(expression)
- 式: 最後の行の値を取得する列または計算結果を指定する式。
2. 実行構文
select
*,
last_value(money) over(partition by user_id order by join_time) as alias_last_value
from order_for_goods;
- ユーザーが指定された時間範囲のデータを持っていない場合、LAST_VALUE() 関数はデフォルト値の NULL を返すことに注意してください。
2. 実行結果
3. 説明する
- LAST_VALUE() がウィンドウの最後の値を取得していないことがわかります。ウィンドウは user_id によってパーティション化され、join_time 列によってソートされています。1001 パーティションの金額を 1800.90 に返すのが合理的です。なぜですか? なぜですか?
- その理由は、LAST_VALUE() のデフォルトの統計範囲が、境界のない前の行と現在の行の間の行であるためです。
3. 検証
select
*,
last_value(money) over(partition by user_id order by join_time) as alias_last_value1,
last_value(money) over(partition by user_id order by join_time rows between unbounded preceding and current row) as alias_last_value2,
last_value(money) over(partition by user_id order by join_time rows between unbounded preceding and unbounded following) as alias_last_value3
from order_for_goods;
- エイリアス alias_last_value2 は、LAST_VALUE() のデフォルトの統計範囲が、境界のない前の行と現在の行の間の行であることを検証していることがわかります (つまり、計算は境界なしで現在の行から先へ実行されます。つまり、すべての行の結果)現在の行が計算される前に。)
- エイリアス alias_last_value3 は、境界のない先行と境界のない後続の間の行を指定していることがわかります (境界なしで現在の行から前後に計算が実行されることを示します。つまり、パーティション全体の計算の結果)。統計、user_id パーティション、join_time 列がソートされ、1001 パーティションのフィールド Money の最後のトランザクション金額が 1800.90 として返されます。
+----------+---------+---------+----------+---------------------+------------------+------------------+------------------+
| order_id | user_id | money | quantity | join_time | alias_last_value | alias_last_value | alias_last_value |
+----------+---------+---------+----------+---------------------+------------------+------------------+------------------+
| 34 | 1001 | 1000.10 | 6 | 2023-01-08 00:00:00 | 1000.10 | 1000.10 | 1800.90 |
| 33 | 1001 | 3600.89 | 5 | 2023-05-02 00:00:00 | 3600.89 | 3600.89 | 1800.90 |
| 32 | 1001 | 1800.90 | 1 | 2023-06-07 00:00:00 | 1800.90 | 1800.90 | 1800.90 |
| 36 | 1002 | 4500.99 | 1 | 2023-03-14 00:00:00 | 2500.90 | 4500.99 | 1100.90 |
| 38 | 1002 | 2500.90 | 1 | 2023-03-14 00:00:00 | 2500.90 | 2500.90 | 1100.90 |
| 35 | 1002 | 1100.90 | 9 | 2023-04-07 00:00:00 | 1100.90 | 1100.90 | 1100.90 |
| 40 | 1003 | 2500.90 | 2 | 2022-09-08 00:00:00 | 2500.90 | 2500.90 | 2500.10 |
| 39 | 1003 | 2500.90 | 1 | 2022-12-12 00:00:00 | 2500.90 | 2500.90 | 2500.10 |
| 41 | 1003 | 6000.90 | 8 | 2023-01-10 00:00:00 | 6000.90 | 6000.90 | 2500.10 |
| 37 | 1003 | 2500.10 | 3 | 2023-02-14 00:00:00 | 2500.10 | 2500.10 | 2500.10 |
+----------+---------+---------+----------+---------------------+------------------+------------------+------------------+
10 rows in set (0.00 sec)
4.7. その他の機能
4.7.1、NTILE()関数
1. 文法的説明
- NTILE() は、クエリ結果セットを指定された数のバケットに分割し、バケットのサイズに従って各バケットにデータを分散するために使用されます。
NTILE(bucket_size)
- bucket_size: 結果セットを分割するバケットの数を示す整数パラメータ。
2. ステートメントを実行します
select
*,
ntile(1) over(partition by user_id order by join_time desc) as alias_ntile1,
ntile(2) over(partition by user_id order by join_time desc) as alias_ntile2,
ntile(3) over(partition by user_id order by join_time desc) as alias_ntile3
from order_for_goods;
- このクエリでは、ウィンドウ関数 NTILE() を使用します。これは、データ セットを指定された数のバケットに均等に分散し、各行が属するバケット番号を返すことができます。
- エイリアス「alias_ntile3」を例にすると、このクエリでは、ntile(3) は各ユーザーを 3 つのグループに分割することを意味し、partition by user_id は user_id でグループ化することを意味し、order by join_time desc は join_time で降順にソートすることを意味します。
- ntile(2) の場合は 2 つのグループに分けることを意味し、ntile(1) の場合は 1 つのグループに分けることを意味します。
3. 実行結果
説明: NTILE() 関数は、順序付けされたデータセットを指定された数のバケットに均等に分散し、各行にバケット番号を割り当てることができます。均等に分散できない場合は、バケット番号の小さいバケットに追加の行が割り当てられ、各バケットに配置できる行数の差は最大 1 になります。
4.7.2、NTH_VALUE()関数
1. 文法的説明
- NTH_VALUE() 関数は、順序付けされたデータ セット内の指定された位置の値を計算するために SQL で使用されるウィンドウ関数です。
NTH_VALUE(expression, nth_parameter)
- 式: 評価する式。単一の値に評価されます。
- nth_parameter: 計算する値のシーケンス番号を示す整数パラメータです。
2. ステートメントを実行します
select
*,
nth_value(money, 2) over(partition by user_id order by join_time ) as alias_nth_value
from order_for_goods;
- ユーザーが指定された時間範囲内にデータを持たない場合、NTH_VALUE() 関数はデフォルト値 NULL を返すことに注意してください。
3. 実行結果