WITH RankedSales AS (
SELECT
employee,
department,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales
)
SELECT
employee,
department,
sale
FROM
RankedSales
WHERE
rank <= 3;
DELETE FROM sales
WHERE id IN (
SELECT id
FROM (
SELECT
id,
ROW_NUMBER() OVER (PARTITION BY employee ORDER BY sale DESC) AS rank
FROM
sales
) tmp
WHERE rank > 1
);
MySQL 8.0では、ROW_NUMBER()のほかにも、ランキング付けや順位計算に使用できるウィンドウ関数としてRANK()やDENSE_RANK()が用意されています。これらの関数は似たような役割を持ちながら、動作や結果に違いがあります。ここでは、それぞれの関数を比較しながら、適切な使用場面を解説します。
MySQL 8.0より前のバージョンでは、ROW_NUMBER()関数やその他のウィンドウ関数がサポートされていません。しかし、ユーザー変数を活用することで、似たような機能を実現できます。ここでは、MySQL 8.0未満での代替手法を具体的に解説します。
5-1. ユーザー変数を利用した連番付け
MySQL 5.7以前の環境では、ユーザー変数を使って行ごとに連番を付けることが可能です。以下の例を見てみましょう。
使用例: 部門ごとの売上ランキング
データ例:
employee
department
sale
A
営業部
500
B
営業部
800
C
開発部
600
D
開発部
700
クエリ:
SET @row_num = 0;
SET @dept = '';
SELECT
employee,
department,
sale,
@row_num := IF(@dept = department, @row_num + 1, 1) AS rank,
@dept := department
FROM
(SELECT * FROM sales ORDER BY department, sale DESC) AS sorted_sales;
実行結果:
employee
department
sale
rank
B
営業部
800
1
A
営業部
500
2
D
開発部
700
1
C
開発部
600
2
5-2. 上位N件のデータ抽出
上位N件のデータを取得するには、同様にユーザー変数を利用します。 クエリ:
SET @row_num = 0;
SET @dept = '';
SELECT *
FROM (
SELECT
employee,
department,
sale,
@row_num := IF(@dept = department, @row_num + 1, 1) AS rank,
@dept := department
FROM
(SELECT * FROM sales ORDER BY department, sale DESC) AS sorted_sales
) AS ranked_sales
WHERE rank <= 3;
結果:
employee
department
sale
rank
B
営業部
800
1
A
営業部
500
2
D
開発部
700
1
C
開発部
600
2
このクエリでは、部門ごとにランキングを付けた後、上位3件以内のデータのみを抽出しています。
5-3. 重複データの検出と削除
重複データの処理も、ユーザー変数を利用して対応できます。 クエリ例: 重複データの検出
SET @row_num = 0;
SET @id_check = '';
SELECT *
FROM (
SELECT
id,
name,
@row_num := IF(@id_check = name, @row_num + 1, 1) AS rank,
@id_check := name
FROM
(SELECT * FROM customers ORDER BY name, id) AS sorted_customers
) AS tmp
WHERE rank > 1;
クエリ例: 重複データの削除
DELETE FROM customers
WHERE id IN (
SELECT id
FROM (
SELECT
id,
@row_num := IF(@id_check = name, @row_num + 1, 1) AS rank,
@id_check := name
FROM
(SELECT * FROM customers ORDER BY name, id) AS sorted_customers
) AS tmp
WHERE rank > 1
);
5-4. ユーザー変数の注意点
セッション依存性
ユーザー変数はセッション内でのみ有効です。別のクエリやセッションでは利用できません。
処理順序の依存
ユーザー変数はクエリの実行順序に依存するため、ORDER BY句の設定が重要です。
SQLの可読性と保守性
コードが複雑化しやすいため、MySQL 8.0以降ではウィンドウ関数の使用を推奨します。
まとめ
MySQL 8.0未満では、ウィンドウ関数が使用できない代わりにユーザー変数を用いることで、連番付けやランキング処理を実現できます。ただし、クエリが複雑になりやすいため、可能であれば新しいバージョンへの移行を検討するのが望ましいでしょう。
CREATE VIEW ranked_sales AS
SELECT
employee,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales;
ビューを利用することで、最新のデータに基づいたランキングを常に維持できます。
6-4. クエリ例のベストプラクティス
以下は、パフォーマンスと保守性を考慮したベストプラクティスの例です。 例: 上位N件のデータ抽出
WITH RankedSales AS (
SELECT
employee,
department,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales
)
SELECT *
FROM RankedSales
WHERE rank <= 3;