0Pricing
SQL Interview Prep · レッスン

ROW_NUMBERで一意の連番を付ける

各パーティション内で決定的な行番号を割り当てます。

「ROW_NUMBERで一意の連番を付ける」はCoddyKit上の無料SQL Interview Prepレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSQL Interview Prep学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 SQL Interview Prepコースには全4レッスンが含まれています。

ROW_NUMBER が保証すること

ROW_NUMBER() は、ウィンドウの ORDER BY に従って、パーティション内の各行に一意で欠番のない整数を1から割り当てます。順序付けに使う値が同じ場合でも、2つの行が同じ番号になることはありません。

この一意性があるため、「グループごとに1行を選ぶ」問題や重複排除の問題で、面接官はこの関数をよく使います。他のランキング関数(RANK、DENSE_RANK)は、同順位の行に対する一意性を保証しません。

最小構成の ROW_NUMBER クエリ

最も単純な形式では、選択した順序で結果セット全体に番号を振ります。ROW_NUMBER では、OVER 内の ORDER BY が常に必要です。これがないと番号の順序が任意になり、多くのエンジンでは拒否されるか警告が出ます。

ここでは、入社日を降順に並べるため、最も最近入社した従業員が行番号1になります。

SELECT
  name,
  hire_date,
  ROW_NUMBER() OVER (ORDER BY hire_date DESC) AS rn
FROM employees;

パーティション内での番号付け

PARTITION BY を追加すると、各グループ内で独立して行に番号を振れます。パーティションの値が変わるたびにカウンターは1に戻ります。

この例では、各部署に給与順の1、2、3という独自の連番が割り当てられます。各部署で最も給与が高い従業員が番号1になるため、これはグループごとの上位N件を求めるパターンの出発点になります。

SELECT
  department,
  name,
  salary,
  ROW_NUMBER() OVER (
    PARTITION BY department
    ORDER BY salary DESC
  ) AS dept_rn
FROM employees;

ROW_NUMBER と同順位:同順位は発生しない

面接で重要なポイントです。2つの行の順序付けに使う値が同じでも、ROW_NUMBER は異なる番号を付けます。どちらに小さい番号が付くかは、タイブレーカー列を追加しない限り非決定的です。

  • RANK では、同順位の行に同じ順位が付けられます。
  • ROW_NUMBER は、同順位の行の順序を任意に決めます。

結果を再現可能にするには、ORDER BY に一意なタイブレーカーを必ず追加してください。

SELECT
  name,
  salary,
  ROW_NUMBER() OVER (
    ORDER BY salary DESC, employee_id  -- employee_id breaks ties deterministically
  ) AS rn
FROM employees;

決定的なタイブレーカーのルール

ORDER BY が厳密な順序付けになっていない場合(列の組み合わせが一意でない場合)、同じデータに対しても実行のたびに行番号が変わることがあります。面接官は、ページネーションや「最新のレコード」を求める問題にこの落とし穴を仕込みます。

経験則として、結果を安定させる必要がある場合は、最後の並べ替えキーとして主キーやその他の一意な列を追加してください。

-- Unstable: many rows can share the same created_at
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)

-- Stable: id guarantees a single deterministic winner
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC)

実例:顧客ごとの最新注文

定番の質問です。「各顧客の最新の注文を返してください」。顧客ごとに注文を日付の降順で番号付けし、外側のクエリで rn = 1 の行だけを残します。

ROW_NUMBER は各パーティションに番号1の行を必ず1つだけ作るため、顧客ごとに正確に1行を取得できます。2つの注文のタイムスタンプが同じ場合でも、タイブレーカーによって解決されるため重複しません。

SELECT customer_id, order_id, order_date, amount
FROM (
  SELECT
    customer_id, order_id, order_date, amount,
    ROW_NUMBER() OVER (
      PARTITION BY customer_id
      ORDER BY order_date DESC, order_id DESC
    ) AS rn
  FROM orders
) t
WHERE rn = 1;

実例:行の重複排除

ROW_NUMBER は、重複排除に最も適した明快なツールです。重複を定義する列でパーティション化し、優先ルールで並べ替えたうえで、rn = 1 だけを残します。

ここでは、同じメールアドレスを持つ行を重複とみなし、最も早く作成された行を残します。rn > 1 の行はすべて重複なので、削除することも無視することもできます。

SELECT id, email, created_at
FROM (
  SELECT
    id, email, created_at,
    ROW_NUMBER() OVER (
      PARTITION BY email
      ORDER BY created_at ASC, id ASC
    ) AS rn
  FROM users
) t
WHERE rn = 1;

ページネーションにおける ROW_NUMBER

OFFSET/FETCH が広く使われるようになる前は、ROW_NUMBER がページネーションを支えていました。現在でもSQL Serverや多くのETLジョブで使われています。安定した順序で行に番号を付け、外側のクエリで番号の範囲を絞り込みます。

面接での注意点です。ページネーションが正しく機能するのは、順序が決定的な場合だけです。そうでなければ、同じ行が2つのページに現れたり、完全に抜け落ちたりする可能性があります。

SELECT *
FROM (
  SELECT *, ROW_NUMBER() OVER (ORDER BY created_at, id) AS rn
  FROM articles
) t
WHERE rn BETWEEN 21 AND 40;  -- page 2, 20 per page

データを並べ替えるための連番の割り当て

並べ替え後の行に番号を振り直す、1..Nのラベルを作る、生成した系列と行を対応付けるなど、単純な連番インデックスだけが必要な場合があります。目的の順序に対して ROW_NUMBER を使えば、欠番のない、きれいな連番を作れます。

順序付けされていない任意の集合に、後で結合するための安定した位置を与える場合にも、この方法を使います。

SELECT
  ROW_NUMBER() OVER (ORDER BY score DESC, player_id) AS leaderboard_position,
  player_id,
  score
FROM scores;

ROW_NUMBER と COUNT の比較:「N番目の行」

「最新から3番目の注文」や「2番目の行」を求めるように言われたら、ROW_NUMBER を使い、外側のクエリで正確な値に絞り込みます。番号が一意なので、rn = 3 は必ず1行を返します。

RANK と比較してみましょう。2番目の異なる値(たとえば同順位を含めた2番目に高い給与)が必要な場合、ROW_NUMBER は適切なツールではありません。その場合は、次のレッスンで扱う DENSE_RANK が必要です。

SELECT order_id, order_date
FROM (
  SELECT order_id, order_date,
         ROW_NUMBER() OVER (ORDER BY order_date DESC, order_id DESC) AS rn
  FROM orders
) t
WHERE rn = 3;  -- exactly the 3rd most recent order

落とし穴のまとめ

面接でプレッシャーがかかっても、次の点を正しく使い分けてください。

  • ROW_NUMBER は、パーティション内で常に一意かつ欠番がありません。
  • OVER 内の ORDER BY が必要です。一意なタイブレーカーがない場合、同順位では結果が非決定的になります。
  • WHERE では絞り込めないため、サブクエリまたはCTEでラップします。
  • 1グループ1行の取得、重複排除、ページネーションにはこれを使い、同順位に同じ番号を付ける必要がある場合は DENSE_RANK を使います。

理解度チェック

ORDER BY の値が同じ行を、ROW_NUMBER はどのように扱いますか?

まとめ:決定的な連番付け

ROW_NUMBER は、パーティション内で一意かつ欠番のない番号を付けるためのツールです。次のことを学びました。

  • 集合全体と、パーティションごとのグループに番号を付ける。
  • 一意なタイブレーカーを追加して、決定的で再現可能な結果にする。
  • 外側のクエリで rn を絞り込むことで、キーごとの最新行、重複排除、ページネーション、N番目の行の問題を解決する。

次は、RANK と DENSE_RANK が意図的に同順位へ同じ番号を付ける仕組みと、両者の欠番の違いを見ていきます。

よくある質問

「ROW_NUMBERで一意の連番を付ける」レッスンは無料ですか?

はい。「ROW_NUMBERで一意の連番を付ける」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、SQL Interview Prepコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 SQL Interview Prepコースには全4レッスンが含まれています。

「ROW_NUMBERで一意の連番を付ける」で何を学びますか?

各パーティション内で決定的な行番号を割り当てます。 ブラウザで直接実行するハンズオンコードでSQL Interview Prepを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

SQL Interview Prepを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSQL Interview Prepは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「ROW_NUMBERで一意の連番を付ける」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSQL Interview Prepレッスンでコードを書いて実行できますか?

はい。すべてのSQL Interview Prepレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. OVER、PARTITION BY、ORDER BY
  2. ROW_NUMBERで一意の連番を付ける
  3. 同順位におけるRANKとDENSE_RANKの違い
  4. ウィンドウ関数の結果でフィルタリングする
← SQL Interview Prepに戻る