ランキングや累積合計も自由自在!SQL ServerのROW_NUMBER関数でデータ分析をレベルアップ
SQL Server における ROW_NUMBER() 関数と WHERE 句での活用
SQL Server の ROW_NUMBER()
関数は、ウィンドウ関数と呼ばれる特殊な関数の一種であり、特定の条件に基づいて結果セット内の行に順位や番号を付与することができます。一方、WHERE
句は、SELECT ステートメントで取得する行を絞り込むために使用される句です。
本記事では、ROW_NUMBER()
関数と WHERE
句を組み合わせることで実現できる高度なデータ操作について、詳細な解説と具体的な例を交えてご紹介します。
ROW_NUMBER() 関数の概要
ROW_NUMBER()
関数は、OVER 句と組み合わせて使用することで、様々な条件に基づいて行に番号を付与することができます。基本的な構文は以下の通りです。
ROW_NUMBER() OVER (PARTITION BY 分割列 ORDER BY 順序列)
- PARTITION BY 分割列: 結果セットをグループ化する列を指定します。複数列をカンマ区切りで指定することも可能です。
例: 顧客テーブル customers
において、各都道府県ごとの購入金額の合計額が最も大きい顧客を抽出する
SELECT
c.customer_id,
c.prefecture,
SUM(o.order_amount) AS total_amount,
ROW_NUMBER() OVER (PARTITION BY c.prefecture ORDER BY total_amount DESC) AS rank
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.prefecture
HAVING rank = 1;
WHERE 句との連携による高度なデータ操作
ROW_NUMBER()
関数で付与された行番号を活用することで、WHERE
句においてより高度な条件設定が可能になります。具体的には、以下のような操作を実現できます。
1 特定の順位の行のみを取得
ROW_NUMBER()
関数の結果を用いて、特定の順位の行のみを抽出することができます。
例: 上記の例で、各都道府県ごとに 2 番目に購入金額が多い顧客を抽出する
SELECT
c.customer_id,
c.prefecture,
SUM(o.order_amount) AS total_amount,
ROW_NUMBER() OVER (PARTITION BY c.prefecture ORDER BY total_amount DESC) AS rank
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.prefecture
HAVING rank = 2;
2 特定の範囲内の順位の行を取得
WHERE
句を用いて、順位の範囲を指定することで、より柔軟なデータ抽出が可能になります。
SELECT
c.customer_id,
c.prefecture,
SUM(o.order_amount) AS total_amount,
ROW_NUMBER() OVER (PARTITION BY c.prefecture ORDER BY total_amount DESC) AS rank
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.prefecture
HAVING rank BETWEEN 3 AND 5;
3 累積合計の算出
ROW_NUMBER()
関数と SUM()
関数を組み合わせて、累積合計を算出することができます。
SELECT
c.prefecture,
SUM(o.order_amount) AS total_amount,
SUM(o.order_amount) OVER (PARTITION BY c.prefecture ORDER BY o.order_date) AS cumulative_amount
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
GROUP BY c.prefecture, o.order_date
ORDER BY c.prefecture, o.order_date;
ROW_NUMBER() 関数と WHERE 句の注意点
ROW_NUMBER()
関数は、サブクエリ内で使用する必要があります。- `WHERE
サンプルコード:顧客テーブルにおける分析例
テーブル構造
-- 顧客テーブル (customers)
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
prefecture VARCHAR(2) NOT NULL
);
-- 注文テーブル (orders)
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
order_amount DECIMAL(10,2) NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
分析内容
- 各都道府県における顧客数の合計と、顧客数の多い順の都道府県ランキング
- 各顧客の累積購入金額と、購入金額の多い順の顧客ランキング
- 2023年12月に注文金額が最も多かった顧客とその注文詳細
コード
-- 1. 各都道府県における顧客数の合計と、顧客数の多い順の都道府県ランキング
SELECT
c.prefecture,
COUNT(*) AS customer_count,
ROW_NUMBER() OVER (ORDER BY customer_count DESC) AS rank
FROM customers AS c
GROUP BY c.prefecture
ORDER BY rank;
-- 2. 各顧客の累積購入金額と、購入金額の多い順の顧客ランキング
SELECT
c.customer_id,
c.name,
SUM(o.order_amount) AS total_amount,
ROW_NUMBER() OVER (ORDER BY total_amount DESC) AS rank
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name
ORDER BY rank;
-- 3. 2023年12月に注文金額が最も多かった顧客とその注文詳細
SELECT
c.customer_id,
c.name,
o.order_id,
o.order_date,
o.order_amount
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
WHERE o.order_date >= '2023-12-01' AND o.order_date <= '2023-12-31'
ORDER BY o.order_amount DESC
LIMIT 1;
説明
ROW_NUMBER()
関数を使用して、顧客数の多い順に順位を付与しています。ORDER BY customer_count DESC
で、顧客数の多い順にソートしています。
- 顧客ごとに累積購入金額を算出するために、
ROW_NUMBER()
関数とSUM()
関数を組み合わせています。
WHERE
句を使用して、2023年12月の注文のみを抽出しています。LIMIT 1
で、上位1件のみを取得しています。
補足
上記のコードはあくまでも例であり、分析対象や目的に合わせて自由にカスタマイズすることができます。
ROW_NUMBER()
関数と WHERE
句を組み合わせることで、SQL Server において高度なデータ分析を実現することができます。これらの機能を理解することで、より深い洞察を得ることが可能となります。
- [そろそろSQLのウィンドウ関数を使ってみよう! 連載3/3話
ROW_NUMBER() 関数以外の代替方法
サブクエリを使用することで、複雑な条件に基づいて行を絞り込むことができます。
例: 各都道府県における購入金額の合計額が最も大きい顧客を抽出する
SELECT c.customer_id, c.prefecture
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.prefecture
HAVING SUM(o.order_amount) = (
SELECT MAX(SUM(o2.order_amount))
FROM customers AS c2
JOIN orders AS o2 ON c2.customer_id = o2.customer_id
GROUP BY c2.customer_id
);
ウィンドウ関数以外の集計関数
RANK()
や DENSE_RANK()
などのウィンドウ関数以外にも、MAX()
や MIN()
などの集計関数を使用して、条件を満たす行を抽出することができます。
SELECT c.customer_id, c.prefecture
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.prefecture
HAVING SUM(o.order_amount) = MAX(SUM(o.order_amount)) OVER (PARTITION BY c.prefecture);
結合
複数のテーブルを結合することで、条件を満たす行を抽出することができます。
SELECT c.customer_id, c.prefecture
FROM customers AS c
JOIN (
SELECT customer_id, MAX(SUM(order_amount)) AS max_total_amount
FROM orders
GROUP BY customer_id
) AS max_orders ON c.customer_id = max_orders.customer_id;
CTE (Common Table Expression)
CTE を使用することで、複雑なクエリをより分かりやすく記述することができます。
WITH customer_totals AS (
SELECT customer_id, prefecture, SUM(order_amount) AS total_amount
FROM customers
JOIN orders ON customers.customer_id = orders.customer_id
GROUP BY customer_id, prefecture
)
SELECT c.customer_id, c.prefecture
FROM customers AS c
JOIN customer_totals AS ct ON c.customer_id = ct.customer_id
WHERE ct.total_amount = (
SELECT MAX(total_amount)
FROM customer_totals
);
それぞれの方法の比較
方法 | 利点 | 欠点 |
---|---|---|
サブクエリ | 柔軟性が高い | 複雑になりやすい |
ウィンドウ関数以外の集計関数 | シンプル | サブクエリよりも柔軟性に劣る |
結合 | シンプル | 複数のテーブルが必要になる場合がある |
CTE | 複雑なクエリを分かりやすく記述できる | サブクエリよりも冗長になる場合がある |
どの方法が最適かは、状況によって異なります。それぞれの方法の利点と欠点を理解し、目的に合った方法を選択することが重要です。
sql sql-server t-sql