【保存版】SQLのRANK関数を徹底解説|順位付けの基本・DENSE_RANKとの違いもわかりやすく解説

その他

「売上トップ3を表示したい」
「同点の場合は同じ順位にしたい」
──こういった「順位付け」は、データ分析でよくあるニーズです。

SQLでは、これを簡単に実現できるのがRANK()関数です。

この記事では、SQLのRANK関数の使い方や仕組み、DENSE_RANK・ROW_NUMBERとの違い、実務での応用例まで、初心者にもわかりやすく解説します。

スポンサーリンク

RANK関数って何?基本を理解しよう

RANK関数の役割

RANK()は、指定した条件で順位(ランク)をつけるウィンドウ関数です。

同順位がある場合は次の順位がスキップされるという特徴があります。

基本的な書き方

SELECT 列名,
       RANK() OVER (ORDER BY ソート対象の列 DESC) AS 順位
FROM テーブル名;

実際の使用例

学生の成績順位

SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

結果例

+----------+-------+------+
| name     | score | rank |
+----------+-------+------+
| 田中太郎 | 95    | 1    |
| 佐藤花子 | 90    | 2    |
| 山田次郎 | 90    | 2    |  ← 同点で2位
| 鈴木一郎 | 85    | 4    |  ← 3位がスキップされて4位
+----------+-------+------+

RANK関数の特徴

同点の扱い

  • 同じ値の場合、同じ順位になる
  • 次の順位はスキップされる(上の例:2位が2人いるので、次は4位)

ソート順の指定

  • DESC:大きい順(1位が最大値)
  • ASC:小さい順(1位が最小値)

RANK、DENSE_RANK、ROW_NUMBERの違い

3つの関数の動作比較

同じデータ(得点:100, 100, 90, 80)に対して、各関数がどんな順位をつけるか見てみましょう。

名前得点RANK()DENSE_RANK()ROW_NUMBER()
Aさん100111
Bさん100112
Cさん90323
Dさん80434

それぞれの特徴

RANK():スキップありの順位

SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;
  • 同点は同じ順位
  • 次の順位はスキップされる
  • 結果:1, 1, 3, 4

DENSE_RANK():スキップなしの順位

SELECT name, score,
       DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank
FROM students;
  • 同点は同じ順位
  • 次の順位はスキップされない
  • 結果:1, 1, 2, 3

ROW_NUMBER():完全な連番

SELECT name, score,
       ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num
FROM students;
  • 同点でも違う番号
  • 完全に1から連続する番号
  • 結果:1, 2, 3, 4

どれを使うべき?

RANK()が適している場面

  • スポーツの順位(3位の次は5位になるのが自然)
  • 一般的なランキング表示

DENSE_RANK()が適している場面

  • 評価段階(S、A、B、Cのような段階評価)
  • 連続した順位が必要な場合

ROW_NUMBER()が適している場面

  • 一意の番号が必要
  • ページ分割(1件目、2件目…)

実際の仕事で使える応用例

売上ランキングの作成

-- 営業成績のランキング
SELECT 
    employee_name,
    total_sales,
    RANK() OVER (ORDER BY total_sales DESC) AS sales_rank
FROM sales_summary
WHERE year = 2025;

結果例

+--------------+-------------+------------+
| employee_name | total_sales | sales_rank |
+--------------+-------------+------------+
| 田中営業     | 5000000     | 1          |
| 佐藤営業     | 4500000     | 2          |
| 山田営業     | 4500000     | 2          |
| 鈴木営業     | 4000000     | 4          |
+--------------+-------------+------------+

部署ごとの順位付け

-- 部署内での成績順位
SELECT 
    department,
    employee_name,
    score,
    RANK() OVER (PARTITION BY department ORDER BY score DESC) AS dept_rank
FROM employee_evaluations;

結果例

+------------+---------------+-------+-----------+
| department | employee_name | score | dept_rank |
+------------+---------------+-------+-----------+
| 営業部     | 田中          | 95    | 1         |
| 営業部     | 佐藤          | 90    | 2         |
| 営業部     | 山田          | 85    | 3         |
| 開発部     | 鈴木          | 98    | 1         |
| 開発部     | 高橋          | 92    | 2         |
+------------+---------------+-------+-----------+

PARTITION BYを使うことで、グループごとに個別の順位をつけることができます。

商品の人気ランキング

-- カテゴリ別の売上ランキング
SELECT 
    category,
    product_name,
    sales_count,
    RANK() OVER (PARTITION BY category ORDER BY sales_count DESC) AS category_rank
FROM product_sales
WHERE month = '2025-06';

上位N件の抽出

-- 各部署の上位3位を抽出
SELECT *
FROM (
    SELECT 
        department,
        employee_name,
        score,
        RANK() OVER (PARTITION BY department ORDER BY score DESC) AS rank
    FROM employee_evaluations
) ranked
WHERE rank <= 3;

複数条件での順位付け

-- 売上高→利益率の順で順位付け
SELECT 
    product_name,
    sales_amount,
    profit_rate,
    RANK() OVER (ORDER BY sales_amount DESC, profit_rate DESC) AS combined_rank
FROM products;

詳しい使用例とテクニック

時系列データでの順位

-- 月別売上での年間順位
SELECT 
    year_month,
    total_sales,
    RANK() OVER (ORDER BY total_sales DESC) AS monthly_rank,
    -- 前年同月比での順位
    RANK() OVER (ORDER BY growth_rate DESC) AS growth_rank
FROM monthly_sales
WHERE year = 2025
ORDER BY year_month;

パーセンタイル順位

-- 偏差値のような相対的な位置を計算
SELECT 
    student_name,
    score,
    RANK() OVER (ORDER BY score DESC) AS rank,
    PERCENT_RANK() OVER (ORDER BY score DESC) AS percentile_rank
FROM test_scores;

範囲での順位分け

-- 成績を段階別に分類
SELECT 
    student_name,
    score,
    CASE 
        WHEN RANK() OVER (ORDER BY score DESC) <= 10 THEN 'S級'
        WHEN RANK() OVER (ORDER BY score DESC) <= 30 THEN 'A級'
        WHEN RANK() OVER (ORDER BY score DESC) <= 60 THEN 'B級'
        ELSE 'C級'
    END AS grade
FROM test_scores;

前回順位との比較

-- 今月と先月の順位変化
SELECT 
    employee_name,
    this_month_sales,
    RANK() OVER (ORDER BY this_month_sales DESC) AS this_month_rank,
    last_month_rank,
    (last_month_rank - RANK() OVER (ORDER BY this_month_sales DESC)) AS rank_change
FROM (
    SELECT 
        e.employee_name,
        t.sales_amount as this_month_sales,
        RANK() OVER (ORDER BY l.sales_amount DESC) AS last_month_rank
    FROM employees e
    JOIN this_month_sales t ON e.id = t.employee_id
    JOIN last_month_sales l ON e.id = l.employee_id
) ranked_data;

よくある間違いと注意点

ORDER BYが必須

間違った例

-- ORDER BYがないとエラーになる
SELECT name, score,
       RANK() OVER () AS rank  -- エラー!
FROM students;

正しい例

SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

NULLの扱いに注意

-- NULLがある場合の動作
SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

NULLの順位

  • 多くのデータベースではNULLは最後になる
  • NULLS FIRSTNULLS LASTで制御可能
-- NULLを最初に持ってくる
SELECT name, score,
       RANK() OVER (ORDER BY score DESC NULLS FIRST) AS rank
FROM students;

WHERE句でのフィルタリング

間違った例

-- WHERE句で順位をフィルタリングできない
SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students
WHERE rank <= 3;  -- エラー!

正しい例

-- サブクエリを使う
SELECT *
FROM (
    SELECT name, score,
           RANK() OVER (ORDER BY score DESC) AS rank
    FROM students
) ranked
WHERE rank <= 3;

同じウィンドウ関数の重複実行

非効率な例

SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank,
       DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank,
       ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num
FROM students;

効率的な例

-- WINDOW句を使って共通化
SELECT name, score,
       RANK() OVER w AS rank,
       DENSE_RANK() OVER w AS dense_rank,
       ROW_NUMBER() OVER w AS row_num
FROM students
WINDOW w AS (ORDER BY score DESC);

データベース別の対応状況

主要データベースでの対応

MySQL(8.0以降)

SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

PostgreSQL

-- PostgreSQLではウィンドウ関数が充実
SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank,
       NTILE(4) OVER (ORDER BY score DESC) AS quartile
FROM students;

SQL Server

SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

Oracle

-- Oracleでは多くのウィンドウ関数をサポート
SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

SQLite(3.25以降)

SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

高度な活用テクニック

CTEとの組み合わせ

-- 共通テーブル式で複雑な順位計算
WITH sales_ranking AS (
    SELECT 
        employee_id,
        total_sales,
        RANK() OVER (ORDER BY total_sales DESC) AS sales_rank
    FROM annual_sales
),
performance_ranking AS (
    SELECT 
        employee_id,
        performance_score,
        RANK() OVER (ORDER BY performance_score DESC) AS perf_rank
    FROM performance_evaluations
)
SELECT 
    e.name,
    sr.sales_rank,
    pr.perf_rank,
    (sr.sales_rank + pr.perf_rank) AS combined_score
FROM employees e
JOIN sales_ranking sr ON e.id = sr.employee_id
JOIN performance_ranking pr ON e.id = pr.employee_id
ORDER BY combined_score;

動的な順位計算

-- 条件に応じた順位付け
SELECT 
    product_name,
    price,
    rating,
    CASE @sort_type
        WHEN 'price' THEN RANK() OVER (ORDER BY price)
        WHEN 'rating' THEN RANK() OVER (ORDER BY rating DESC)
        ELSE RANK() OVER (ORDER BY sales_count DESC)
    END AS dynamic_rank
FROM products;

集計との組み合わせ

-- 上位10%の平均売上を計算
WITH ranked_sales AS (
    SELECT 
        employee_id,
        sales_amount,
        RANK() OVER (ORDER BY sales_amount DESC) AS rank,
        COUNT(*) OVER () AS total_count
    FROM sales_data
)
SELECT AVG(sales_amount) AS top_10_percent_avg
FROM ranked_sales
WHERE rank <= (total_count * 0.1);

パフォーマンスの最適化

インデックスの活用

-- ORDER BYで使用する列にインデックスを作成
CREATE INDEX idx_score ON students(score DESC);

-- より効率的なRANK関数の実行
SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

大量データでの注意点

-- 大量データの場合は適切な制限を
SELECT *
FROM (
    SELECT name, score,
           RANK() OVER (ORDER BY score DESC) AS rank
    FROM large_table
    WHERE category = 'specific_category'  -- 事前に絞り込み
) ranked
WHERE rank <= 100;  -- 必要な分だけ取得

実際のビジネスシーンでの活用

売上分析

-- 四半期別の商品売上ランキング
SELECT 
    quarter,
    product_name,
    sales_amount,
    RANK() OVER (PARTITION BY quarter ORDER BY sales_amount DESC) AS quarterly_rank,
    RANK() OVER (ORDER BY sales_amount DESC) AS overall_rank
FROM quarterly_sales
WHERE year = 2025;

顧客分析

-- 顧客の購入金額別ランキング
SELECT 
    customer_id,
    total_purchase,
    RANK() OVER (ORDER BY total_purchase DESC) AS purchase_rank,
    CASE 
        WHEN RANK() OVER (ORDER BY total_purchase DESC) <= 100 THEN 'VIP'
        WHEN RANK() OVER (ORDER BY total_purchase DESC) <= 500 THEN 'Gold'
        ELSE 'Regular'
    END AS customer_tier
FROM customer_summary;

在庫管理

-- 商品の売れ行き順位と在庫アラート
SELECT 
    product_name,
    sales_velocity,
    current_stock,
    RANK() OVER (ORDER BY sales_velocity DESC) AS velocity_rank,
    CASE 
        WHEN current_stock < (sales_velocity * 7) THEN '要発注'
        ELSE '在庫充分'
    END AS stock_status
FROM inventory_analysis;

まとめ

SQLのRANK関数は、順位付けを必要とするデータ分析において欠かせないウィンドウ関数です。

DENSE_RANKやROW_NUMBERとの違いを正しく理解し、状況に応じて使い分けることで、クエリの精度と表現力が飛躍的に向上します。

重要なポイント

  • RANK():同順位あり、次の順位はスキップ
  • DENSE_RANK():同順位あり、次の順位はスキップなし
  • ROW_NUMBER():完全に一意な連番
  • PARTITION BY:グループごとの順位付け
  • ORDER BY:順位の基準となるソート条件

使い方のコツ

  • 目的に応じて適切な関数を選択する
  • サブクエリを使って上位N件を抽出する
  • インデックスを活用してパフォーマンスを向上させる
  • NULLの扱いに注意する

よく使う場面

  • 売上ランキング
  • 成績順位
  • 人気商品ランキング
  • 顧客の購入金額順位
  • 部署内評価

コメント

タイトルとURLをコピーしました