MariaDB Window Functionsで複雑なデータ分析を簡単に行う方法


MariaDB の Window Functions は、特定の行範囲にわたって集計や分析を実行する機能を提供します。従来の集計関数とは異なり、Window Functions は各行に個別の集計値を算出することができます。これにより、複雑なデータ分析やレポート作成が可能になります。

Window Functions の種類

MariaDB では、以下の種類の Window Functions が提供されています。

  • OFFSET FUNCTIONS
    指定したオフセットに基づいて行を特定します。
    • LAG()
    • LEAD()
  • ANALYTIC FUNCTIONS
    指定した範囲内の集計値を算出します。
    • SUM()
    • AVG()
    • MIN()
    • MAX()
    • COUNT()
    • FIRST_VALUE()
    • LAST_VALUE()
  • RANKING FUNCTIONS
    行の順序に基づいて順位を割り当てます。
    • RANK()
    • DENSE_RANK()
    • ROW_NUMBER()
    • NTILE()
    • PERCENT_RANK()
    • CUME_DIST()

Window Functions の構文

Window Functions は、以下の構文で呼び出されます。

SELECT
  ...
  window_function(expression) OVER (PARTITION BY partition_expression ORDER BY order_expression) AS window_alias
FROM table_name;

構文の説明

  • AS window_alias: 集計結果の別名を指定します。
  • ORDER BY order_expression: 集計の対象となる行を並べ替える列を指定します。
  • PARTITION BY partition_expression: 集計の対象となる行範囲を分割する列を指定します。
  • expression: Window Function に渡す式を指定します。
  • window_function: 使用する Window Function を指定します。

Window Functions の例

以下は、Window Functions を使用したいくつかの例です。

例 1: 各部門の売上合計を算出する

SELECT
  department_id,
  SUM(sales) OVER (PARTITION BY department_id) AS total_sales
FROM sales;

このクエリは、各部門の売上合計を total_sales 列に算出し、結果を department_id 列で分割します。

例 2: 各行の前後 3 件の売上を算出する

SELECT
  product_id,
  sales,
  LAG(sales, 3) OVER (ORDER BY product_id) AS previous_sales,
  LEAD(sales, 3) OVER (ORDER BY product_id) AS next_sales
FROM products;

このクエリは、各行の売上、前 3 件の売上、後 3 件の売上をそれぞれ sales, previous_sales, next_sales 列に算出します。

SELECT
  product_id,
  sales,
  RANK() OVER (ORDER BY sales DESC) AS sales_rank
FROM products;


SELECT
  customer_id,
  order_id,
  order_amount,
  SUM(order_amount) OVER (PARTITION BY customer_id) AS total_order_amount
FROM orders;

説明

  • 結果として、各行には、顧客 ID、注文 ID、注文金額、および顧客ごとの注文合計金額が表示されます。
  • OVER (PARTITION BY customer_id) 句は、SUM 関数が customer_id 列で分割された各パーティションに対して適用されることを指定します。
  • SUM(order_amount) は、各行の order_amount 列の値を合計する集計関数です。
  • PARTITION BY customer_id 句は、集計を顧客ごとに分割することを指定します。
  • このクエリは、orders テーブルから顧客ごとの注文合計金額を算出します。

例 2: 商品カテゴリごとの平均売上金額を算出する

SELECT
  product_category,
  product_id,
  sale_price,
  AVG(sale_price) OVER (PARTITION BY product_category) AS avg_sale_price
FROM sales;

説明

  • 結果として、各行には、商品カテゴリ、商品 ID、販売価格、および商品カテゴリごとの平均販売価格が表示されます。
  • OVER (PARTITION BY product_category) 句は、AVG 関数が product_category 列で分割された各パーティションに対して適用されることを指定します。
  • AVG(sale_price) は、各行の sale_price 列の値の平均を算出する集計関数です。
  • PARTITION BY product_category 句は、集計を商品カテゴリごとに分割することを指定します。
  • このクエリは、sales テーブルから商品カテゴリごとの平均売上金額を算出します。

例 3: 各従業員の過去 7 日間の売上合計を算出する

SELECT
  employee_id,
  sale_date,
  sale_amount,
  SUM(sale_amount) OVER (PARTITION BY employee_id ORDER BY sale_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS total_sales_7days
FROM sales;
  • 結果として、各行には、従業員 ID、販売日、販売金額、および従業員ごとの過去 7 日間の売上合計が表示されます。
  • OVER (PARTITION BY employee_id ORDER BY sale_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) 句は、SUM 関数が employee_id 列で分割された各パーティションに対して、過去 7 日間の売上を合計することを指定します。
  • SUM(sale_amount) は、各行の sale_amount 列の値を合計する集計関数です。
  • ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 句は、集計対象となる行範囲を現在行の前 6 行と現在行に設定することを指定します。
  • ORDER BY sale_date 句は、売上データを販売日順に並べ替えることを指定します。
  • PARTITION BY employee_id 句は、集計を従業員ごとに分割することを指定します。
  • このクエリは、sales テーブルから各従業員の過去 7 日間の売上合計を算出します。


代替方法の例

サブクエリ

サブクエリを使用して、関連する行の集計値を算出することができます。


SELECT
  customer_id,
  order_id,
  order_amount,
  (SELECT SUM(order_amount) FROM orders AS o2 WHERE o2.customer_id = o.customer_id) AS total_order_amount
FROM orders AS o;

説明

  • total_order_amount 列は、内部クエリの結果を格納します。
  • 内部クエリは、customer_id が一致するすべての注文の order_amount 列の値を合計します。
  • 外部クエリは、orders テーブルから各行の注文情報を取得します。
  • このクエリは、orders テーブルから顧客ごとの注文合計金額を算出します。

カーソル

カーソルを使用して、行をループ処理し、集計値を算出することができます。


DECLARE cursor_orders IS
  FOR
    SELECT customer_id, order_id, order_amount
    FROM orders;
DECLARE total_order_amount INT;

OPEN cursor_orders;

LOOP
  FETCH cursor_orders INTO @customer_id, @order_id, @order_amount;

  IF @customer_id IS NULL THEN
    LEAVE LOOP;
  END IF;

  SET total_order_amount = total_order_amount + @order_amount;

  UPDATE orders
  SET total_order_amount = total_order_amount
  WHERE customer_id = @customer_id;
END LOOP;

CLOSE cursor_orders;

説明

  • UPDATE ステートメントを使用して、total_order_amount 変数の値を orders テーブルの total_order_amount 列に更新します。
  • 各行に対して、total_order_amount 変数に order_amount 列の値を加算します。
  • total_order_amount 変数は、現在の顧客の注文合計金額を格納します。
  • カーソル cursor_orders は、orders テーブルのすべての行をループ処理します。
  • このクエリは、orders テーブルから顧客ごとの注文合計金額を算出します。

集計関数と結合

集計関数と結合を使用して、関連する行の集計値を算出することができます。


SELECT
  o.customer_id,
  o.order_id,
  o.order_amount,
  (SELECT SUM(o2.order_amount) FROM orders AS o2 WHERE o2.customer_id = o.customer_id) AS total_order_amount
FROM orders AS o
LEFT JOIN (
  SELECT customer_id, SUM(order_amount) AS total_order_amount
  FROM orders
  GROUP BY customer_id
) AS o_total
ON o.customer_id = o_total.customer_id;

説明

  • total_order_amount 列は、o_total サブクエリの結果を格納します。
  • o_total サブクエリは、orders テーブルから顧客ごとの注文合計金額を算出します。
  • orders テーブルと o_total サブクエリを customer_id 列で結合します。
  • このクエリは、orders テーブルから顧客ごとの注文合計金額を算出します。

どの代替方法が最適かは、データの量、クエリのパフォーマンス、および開発者の好みによって異なります。

Window Functions の利点

  • 複雑な集計や分析を簡単に実行できる
  • コードが簡潔で読みやすい

Window Functions の欠点

  • すべてのバージョンの MariaDB でサポートされているわけではない
  • パフォーマンスが劣化する可能性がある

代替方法の利点

  • すべてのバージョンの MariaDB でサポートされている
  • パフォーマンスが優れている可能性がある
  • Window Functions ほど簡単に複雑な集計や分析を実行できない
  • コードが複雑で読みづらい