ROW_NUMBERで一意の連番を付ける
各パーティション内で決定的な行番号を割り当てます。
「ROW_NUMBERで一意の連番を付ける」はCoddyKit上の無料Coding Interview Prepレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはCoding Interview Prep学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Coding 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チューター)、Coding Interview Prepコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Coding Interview Prepコースには全4レッスンが含まれています。
「ROW_NUMBERで一意の連番を付ける」で何を学びますか?
各パーティション内で決定的な行番号を割り当てます。 ブラウザで直接実行するハンズオンコードでCoding Interview Prepを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Coding Interview Prepを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのCoding Interview Prepは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「ROW_NUMBERで一意の連番を付ける」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このCoding Interview Prepレッスンでコードを書いて実行できますか?
はい。すべてのCoding Interview Prepレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- OVER、PARTITION BY、ORDER BY
- ROW_NUMBERで一意の連番を付ける
- 同順位におけるRANKとDENSE_RANKの違い
- ウィンドウ関数の結果でフィルタリングする