【Oracle】ROWNUMとROW_NUMBERの違いと使い分け

【Oracle】ROWNUMとROW_NUMBERの違いと使い分け

記事の文字数:3300

Oracleでよく利用されるROWNUMとROW_NUMBER。どちらも行番号を扱うための仕組みですが、その仕組みや使いどころには大きな違いがあります。本記事では、両者の基本的な特徴や動作の仕組み、ORDER BY との関係、分析関数としての柔軟な使い方まで徹底解説します。

はじめに:Oracleにおける行番号の役割

行番号が必要となる背景(ページングやソートの要件)

Webアプリケーションや業務システムでは、大量データを一度に表示するのではなく「ページング」によって分割表示することが一般的です。その際に必要となるのが「行番号」です。また、ランキングや順位付けのように、特定の並び順を伴う処理でも行番号は不可欠です。たとえば「売上トップ10の社員を表示」「各部署ごとに給与の順位を付ける」といった要件は日常的に発生します。

Oracleでの行番号付与の手段:ROWNUMとROW_NUMBER()

Oracleには古くから存在する ROWNUM という擬似列と、分析関数として利用できる ROW_NUMBER() の2つの手段があります。両者は似た用途で使われる一方、動作原理や適用場面には明確な違いがあります。特に ROWNUM はシンプルで軽量、ROW_NUMBER() は柔軟性が高く標準SQL準拠という特徴があります。

他データベースの機能(LIMIT/TOP)とOracleの行番号

MySQLやPostgreSQLには LIMIT 句、SQL Serverには TOP 句が存在し、シンプルに行数を制限できます。一方でOracleは、歴史的に ROWNUM を利用してきましたが、Oracle 12c以降では OFFSET/FETCH 句が導入され、標準SQLに近い形でページングが可能になりました。これにより、異なるDB製品間でのSQL移植性が向上しています。

ROWNUM と ROW_NUMBERの確認用データ

本記事で紹介するSQLは以下データを利用して確認することを前提とします。

CREATE TABLE employees (
employee_id NUMBER PRIMARY KEY,
department_id NUMBER,
employee_name VARCHAR2(50),
salary NUMBER
);
INSERT INTO employees VALUES (1, 10, 'Alice', 5000);
INSERT INTO employees VALUES (2, 10, 'Bob', 6000);
INSERT INTO employees VALUES (3, 10, 'Charlie', 7000);
INSERT INTO employees VALUES (4, 20, 'David', 4000);
INSERT INTO employees VALUES (5, 20, 'Ellen', 4500);
INSERT INTO employees VALUES (6, 20, 'Frank', 8000);
INSERT INTO employees VALUES (7, 30, 'Grace', 3000);
INSERT INTO employees VALUES (8, 30, 'Henry', 3500);
INSERT INTO employees VALUES (9, 30, 'Ivy', 9000);
INSERT INTO employees VALUES (10, 30, 'Jack', 7500);
COMMIT;

ROWNUM の基本と特性

ROWNUM の定義:擬似列(Pseudocolumn)

ROWNUM はOracle独自の擬似列で、テーブルに存在しないものの、SQLの実行時に付与される行番号です。擬似列であるため、ユーザーが自由に更新や参照をするわけではなく、Oracleエンジンがクエリ実行時に割り当てます。

ROWNUM の基本的な使い方(最初のN行を取得)

SELECT *
FROM employees
WHERE ROWNUM <= 3;

上記のように ROWNUM を条件にすることで、最初の3行を簡単に取得できます。これは「クエリの途中打ち切り」に近い動作をするため、高速に処理できるという利点があります。

ROWNUM の動作の仕組み(行番号の付与タイミング)

行がフェッチされるタイミングで逐次 ROWNUM が付与されます。そのため、行の並び順に影響されず「読み出された順」で番号が振られます。この特徴は、直感的に想像される「ソート後の順序番号」とは異なるため、利用時には注意が必要です。

ROWNUM の最大の注意点:ORDER BY との評価順序

  • ROWNUMORDER BY よりも先に作用します。
  • そのため、以下のSQLは「ソート後の上位3件」ではなく「抽出順の上位3件」を返します。
SELECT *
FROM employees
WHERE ROWNUM <= 3
ORDER BY salary DESC;

意図した順序で上位N件を取得するにはサブクエリが必要です。

SELECT *
FROM (
SELECT * FROM employees ORDER BY salary DESC
)
WHERE ROWNUM <= 3;

また「特定の番号のみ取得(例:5番目の行)」といった用途も、必ずサブクエリが必要です。つまり、ROWNUM はシンプルながらも柔軟性に欠けるため、ソートや特定範囲取得には工夫が必要となります。

ROW_NUMBER() の基本と特性

ROW_NUMBER() の定義:分析関数(Analytic Function/ウィンドウ関数)

ROW_NUMBER() は分析関数であり、指定した並び順に基づいて論理的な行番号を付与できます。分析関数は OVER() 句を伴い、ソートやグループ単位の処理を柔軟に指定できるのが特徴です。

ROW_NUMBER() の基本的な構文とOVER() 句

SELECT employee_id, salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS rn
FROM employees;

この例では給与の高い順に行番号が振られます。意図した順序で確実に番号が振られるため、ROWNUM と異なり直感的な挙動になります。

柔軟な順序制御:ORDER BY 句の利用

OVER(ORDER BY ...) で自由に並び順を指定できるため、任意のランキングを作成可能です。例えば「入社日の早い順」「名前のアルファベット順」など、さまざまな並び替え基準を簡単に設定できます。

グループごとの連番付与:PARTITION BY 句の利用

SELECT department_id, employee_id, salary,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank
FROM employees;

各部門ごとに連番を付与できる点は、ROWNUM にはできない強力な機能です。実務では「部署ごとの売上ランキング」「カテゴリごとの順位付け」などで頻繁に利用されます。

ROW_NUMBER() の応用例(Top-N、Bottom-N レポート)

上位3人の給与を部門ごとに取得:

SELECT *
FROM (
SELECT e.*, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) rn
FROM employees e
)
WHERE rn <= 3;

同様に、最下位のデータ(Bottom-N)も簡単に取得可能です。柔軟性の高さが最大の魅力といえます。

ROWNUM と ROW_NUMBER() の決定的な違い(比較表を含む)

項目ROWNUMROW_NUMBER()
種類擬似列分析関数
並び順制御不可(サブクエリ必須)ORDER BY で柔軟に可能
グループごと連番不可PARTITION BY で可能
標準SQL準拠非準拠ANSI SQL準拠
用途単純な行制限ページング、ランキング
可読性シンプルだが誤解を招きやすい明示的で意図が伝わりやすい

この比較からわかるように、両者は似ているようで本質的には異なる機能です。ROWNUM は「簡易的なフィルタリング」、ROW_NUMBER() は「柔軟な分析」を目的としています。

可読性・メンテナンス性からの比較

ROW_NUMBER() は直感的でSQLの意図を明確に表現できるため、保守性に優れています。実務では、後からSQLを読んだ人がすぐ理解できることが重要であり、多少の速度差よりも可読性を優先するケースが多いです。

Oracle 12c以降のページング構文

OFFSET/FETCH 句が導入され、以下のように書けます。

SELECT *
FROM employees
ORDER BY salary DESC
OFFSET 5 ROWS FETCH NEXT 3 ROWS ONLY;

この構文は、アプリケーションのページング処理と相性が良く、フロントエンドから「5件スキップして次の3件取得」といったリクエストをそのままSQLに反映できます。内部的には ROW_NUMBER() を用いたサブクエリ展開と同等の動作をします。

使い分けのベストプラクティス

  • 基本は ROW_NUMBER() を使用:読みやすく柔軟で、ほとんどのケースで推奨。
  • 速度優先で単純な先頭N件取得なら ROWNUM を検討。ただし誤用に注意。
  • 12c以降は OFFSET/FETCH を優先:アプリケーション開発者にとって自然な構文。

ROWNUM と ROW_NUMBER() の違いまとめ

ROWNUM と ROW_NUMBER() の主な特徴の再確認

  • ROWNUM:シンプルだが柔軟性に欠け、ORDER BYとの組み合わせに注意が必要。
  • ROW_NUMBER():柔軟で標準SQL準拠、保守性と可読性に優れる。
  • OFFSET/FETCH:12c以降の推奨構文で、ページング処理を簡潔に記述可能。

開発バージョンによる選択肢(11g以前 vs 12c以降)

  • 11g以前ROWNUM または ROW_NUMBER() を用途に応じて使い分け。
  • 12c以降OFFSET/FETCH を利用することで、よりシンプルかつ標準的な書き方が可能。

本記事では、Oracleにおける ROWNUMROW_NUMBER() の違いを整理し、実務における使い分けのポイントを解説しました。単純な行制限から高度なランキングまで幅広く対応できるこれらの機能を理解することで、用途やバージョンに応じた最適な選択が可能になります。開発現場では、性能だけでなく保守性・可読性も考慮し、最適なアプローチを選ぶことが重要です。


以上で本記事の解説を終わります。
よいITライフを!
Scroll to Top