Oracle ROWNUM の使い方と注意点【1行目・連番・WHERE句】

SQL

Oracleデータベースで「上位N件だけデータを取得したい」「結果セットに連番を振りたい」といったニーズはありませんか? OracleROWNUM 擬似列は、まさにそのために使われる機能です。この記事では、ROWNUM の基本的な使い方から、ORDER BY と組み合わせる際の重要な注意点まで、初心者にも分かりやすく解説します。

本記事ではSCOTTスキーマをインストールして使用しています。

SCOTT スキーマをインストール、および接続
$ sqlplus / as sysdba
SQL> @?/rdbms/admin/utlsampl.sql
$ sqlplus scott/tiger

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?


結論:ROWNUMで「上位N件」を取得する最短手順

ROWNUM を使って検索結果の上位N件(例:5件)を取得する最も簡単な方法は、WHERE句で ROWNUM <= N を指定することです。

  1. SELECT 文で必要な列を指定します。
  2. FROM 句で対象テーブルを指定します。
  3. WHERE 句に ROWNUM <= 5 (5件の場合)を追加します。
/* 例:従業員テーブルから「何らかの」5件を取得 */
SELECT ROWNUM, EMPNO, ENAME
FROM SCOTT.EMP
WHERE ROWNUM <= 5;

注意点: この時点では、ORDER BY を指定していないため、どの5件が返されるかは保証されません(DB内部の読み取り順に依存します)。並び順を制御する方法は後述します。


ROWNUMとは? Oracle SQLでの基本的な使い方

ROWNUM は、Oracleデータベースがクエリの結果セットに対して一時的に割り当てる「擬似列(Pseudocolumn)」です。テーブルに実際に格納されているデータではなく、SELECT文がフェッチ(取得)した行に対して、1から順に自動的に割り振られる番号です。

  • 擬似列とは?: テーブル定義には存在しないが、SQL実行時にあたかも列であるかのように参照できる特別な列です(他に ROWID などがあります)。
  • いつ番号が振られる?: WHERE句が評価される「前」、または WHERE句の評価「中」に、条件を満たした行に対して 1, 2, 3… と割り当てられます。
SQL> desc emp  ★rownum 列は存在しない
Name Null? Type
----------------------------------------- -------- ----------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)

SQL> SELECT ROWNUM, EMPNO, ENAME, SAL
2 FROM SCOTT.EMP
3 WHERE ROWNUM <= 5;

ROWNUM EMPNO ENAME SAL
---------- ---------- ---------- ----------
1 7369 SMITH 800
2 7499 ALLEN 1600
3 7521 WARD 1250
4 7566 JONES 2975
5 7654 MARTIN 1250

ROWNUMの主な用途

  1. 上位N件の取得 (Top-N クエリ): WHERE ROWNUM <= 10 のように使い、結果セットを先頭から10件までに制限します。
  2. 連番の付与: SELECT ROWNUM, ... のように使い、結果セットの各行に連番を表示します。

実行例:ROWNUMの正しい使い方と「落とし穴」

ROWNUM は便利な反面、SQLの実行順序を理解していないと意図しない結果を招きます。ここでは SCOTT.EMP テーブル(従業員テーブル)を例に、正しい使い方とよくある間違いを見ていきましょう。

前提: SCOTT ユーザー(または SCOTT.EMP テーブルを参照できるユーザー)で接続していると仮定します。

1. 単純な上位N件の取得

前述の通り、WHERE ROWNUM <= N を使います。

-- SCOTT.EMPテーブルから5件取得する
SELECT ROWNUM, EMPNO, ENAME, SAL
FROM SCOTT.EMP
WHERE ROWNUM <= 5;

実行結果(例):

(注:ORDER BYがないため、この順序は環境や実行計画によって変わる可能性がありますが、一般的にインデックス順などで返されます)

ROWNUMEMPNOENAMESAL
17369SMITH800
27499ALLEN1600
37521WARD1250
47566JONES2975
57654MARTIN1250

2. 【落とし穴】ORDER BY と ROWNUM の実行順序

「給与(SAL)が高い順に5件取得したい」と考え、次のようなSQLを書くと間違いです。

-- ▼▼▼ 間違いの例 ▼▼▼
-- 意図:給与(SAL)で並び替えてから、上位5件を取得したい
SELECT ROWNUM, EMPNO, ENAME, SAL
FROM SCOTT.EMP
WHERE ROWNUM <= 5
ORDER BY SAL DESC;

なぜ間違いか?

SQLの実行順序(論理的な評価順)は、一般的に以下のようになります。

  1. FROM (テーブルの決定)
  2. WHERE (行の絞り込み) ← ここで ROWNUM <= 5 が評価される
  3. GROUP BY (グループ化)
  4. HAVING (グループの絞り込み)
  5. SELECT (列の選択)
  6. ORDER BY (並び替え) ← ROWNUM が決まった後で並び替えられる

つまり、上記のSQLは「まず適当に5件取得し、その5件の中で給与(SAL)が高い順に並び替える」という意味になり、「全従業員の中で給与が高いトップ5」とはなりません。

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?

3. 【正しい】ORDER BY した結果から上位N件を取得する方法

「並び替えた結果」に対して ROWNUM を割り当てるには、サブクエリ(インライン・ビュー)を使います。

-- ▼▼▼ 正しい例 ▼▼▼
-- 給与(SAL)が高い順に並び替えた結果から、上位5件を取得
SELECT ROWNUM, T.*
FROM (
    -- まず、内側のクエリで並び替え(ORDER BY)を行う
    SELECT EMPNO, ENAME, SAL
    FROM SCOTT.EMP
    ORDER BY SAL DESC
) T
WHERE ROWNUM <= 5; -- 並び替え済みの結果に対してROWNUMを適用

実行結果(例):

(SAL=3000のSCOTTとFORDの順序は、ORDER BY SAL DESCだけでは不定になる可能性がありますが、ここでは一例を示します)

※実行結果が異なる

SQL> SELECT ROWNUM, EMPNO, ENAME, SAL
2 FROM SCOTT.EMP
3 WHERE ROWNUM <= 5
4 ORDER BY SAL DESC;

ROWNUM EMPNO ENAME SAL
---------- ---------- ---------- ----------
4 7566 JONES 2975
2 7499 ALLEN 1600
5 7654 MARTIN 1250
3 7521 WARD 1250
1 7369 SMITH 800

SQL> SELECT ROWNUM, T.*
2 FROM (
3 SELECT EMPNO, ENAME, SAL
4 FROM SCOTT.EMP
5 ORDER BY SAL DESC
6 ) T
7 WHERE ROWNUM <= 5;

ROWNUM EMPNO ENAME SAL
---------- ---------- ---------- ----------
1 7839 KING 5000
2 7788 SCOTT 3000
3 7902 FORD 3000
4 7566 JONES 2975
5 7698 BLAKE 2850

トラブルシューティング:ROWNUM > 1 が動かない理由

ROWNUM を使う上で最も混乱しやすいのが、「ROWNUM = 1」や「ROWNUM <= 5」は動作するのに、「ROWNUM = 2」や「ROWNUM > 1」が1行も返さない現象です。

条件式動作理由
WHERE ROWNUM = 1○ (動く)1行目をフェッチし ROWNUM=1 を割り当て。条件(1=1)が真。
WHERE ROWNUM <= 5○ (動く)1行目(1<=5)、2行目(2<=5)…と5行目まで真。6行目で偽。
WHERE ROWNUM = 2× (動かない)1行目をフェッチし ROWNUM=1 を割り当て。条件(1=2)が偽。行が破棄される。次の行をフェッチし、また ROWNUM=1 を割り当て… これを繰り返し、永遠に ROWNUM=2 になる行が現れない。
WHERE ROWNUM > 1× (動かない)上記と同じ理由。ROWNUM=1 の行が WHERE句で破棄され続けるため。
WHERE ROWNUM != 1× (動かない)同上。

一口メモ: ROWNUM は「条件を満たした行に番号を振る」のではなく、「番号を振ってみて、条件を満たさなければその行を捨てる(そして次の行にまた1から番号を振り直そうとする)」という動作をイメージすると理解しやすいです。


運用上の注意点と代替構文 (FETCH FIRST)

ROWNUM は古くから使われてきたOracle固有の構文ですが、Oracle 12c以降では、より直感的でSQL標準に準拠した行制限構文が導入されています。

ROWNUMの注意点

  • 実行順序: ORDER BY との組み合わせにはサブクエリが必須です。
  • ページネーション: 「11行目から20行目まで」といった中間の範囲指定 (ページネーション) を ROWNUM だけで実現するのは非常に複雑です(ROWNUM を含むサブクエリをさらにサブクエリで囲むなど)。

代替構文:FETCH FIRST (OFFSET)

Oracle 12c (19cでも利用可) からは、ORDER BY 句の一部として FETCH 句と OFFSET 句が使えるようになりました。

-- 代替構文 (12c以降)
-- 給与(SAL)が高い順に並び替えて、上位5件を取得
SELECT EMPNO, ENAME, SAL
FROM SCOTT.EMP
ORDER BY SAL DESC
FETCH FIRST 5 ROWS ONLY;

ページネーションの例(11行目から10件取得):

-- 代替構文 (12c以降)
-- 給与(SAL)が高い順に並び替え
-- 10行スキップして(OFFSET)、次の10件を取得(FETCH NEXT)
SELECT EMPNO, ENAME, SAL
FROM SCOTT.EMP
ORDER BY SAL DESC
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;

ROWNUM よりもはるかに直感的に記述できるため、特にページネーション処理(Webアプリケーションなどで必須)を実装する場合は、12c以降の FETCH / OFFSET 構文の使用を強く推奨します。


FAQ (よくある質問)

Q1: ROWNUM と ROWID の違いは何ですか?

A1: ROWNUM はクエリの結果セットに対して一時的に振られる連番(擬似列)です。ROWID はテーブル内の各行が持つ物理的(または論理的)な一意のアドレスを示す擬似列で、行がテーブルに挿入された時点で決まり、削除されるまで不変です。ROWID を使えば、その行に最速でアクセスできます。

Q2: WHERE ROWNUM = 10 がデータを返さないのはなぜですか?

A2: ROWNUM は1から順に割り当てられます。Oracleが1行目を読み込み ROWNUM=1 を割り当てますが、WHERE条件 (1 = 10) が偽 (False) のため、その行は破棄されます。Oracleは次の行を読み込み、また ROWNUM=1 を割り当てます (2にはなりません)。これが繰り返されるため、ROWNUM が10に達することはなく、結果セットは0件となります。

Q3: ROW_NUMBER() 関数との違いは何ですか?

A3: ROWNUM は WHERE句で使われることが多い擬似列です。一方、ROW_NUMBER() は ORDER BY と組み合わせて使用する分析関数(ウィンドウ関数)です。ROW_NUMBER() は ORDER BY で指定された順序に基づいて連番を振るため、サブクエリを使わなくても「並び替え後の連番」を生成できます。ただし、WHERE句で ROW_NUMBER() = 1 のように直接絞り込むことはできず、ROWNUM と同様にサブクエリが必要になります。


まとめ

  • ROWNUM は、Oracleがクエリ結果に一時的に割り当てる擬似列です。
  • 上位N件を取得するには WHERE ROWNUM <= N を使います。
  • WHERE ROWNUM = 1 は動作しますが、WHERE ROWNUM > 1WHERE ROWNUM = N (N>1) は行を返しません。
  • ORDER BY (並び替え)よりも先に ROWNUM が評価されるため、並び替え後の上位N件を取得するにはサブクエリが必須です。
  • Oracle 12c以降では、より直感的な FETCH FIRST N ROWS ONLYOFFSET 句の使用が推奨されます。

本記事は Oracle Database 19c を対象に解説します(他バージョンは画面や既定値が異なる場合があります)。

[参考]
ROWNUM疑似列 – SQL言語リファレンス

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?

コメント

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