Webサイト制作コースのお申し込みはこちら Webサイト制作コースのお申し込みはこちら

OracleのROWNUMについてまとめています。

SQLのROWNUM(Oracle)を使う方法

OracleのROWNUMは、1から始まる連番を取得します。擬似的に列を発生させるため、ROWNUM疑似列と呼ばれます。

SELECT 
  ROWNUM 
, Name
, Age
FROM users

| ROWNUM | Name          | Age  |
| ------ | ------------- | ---- |
| 1      | Yamada Taro   | 10   |
| 2      | Suzuki Ichiro | 30   |
| 3      | Sato Taro     | 20   |
| 4      | Tanaka Jiro   | 50   |
| 5      | Ito Hanako    | 40   |

【関連記事】
【SQL】RONUMで連番を取得!Oracleで連番を取得する方法と注意点を解説

ROWNUMを使って、他のDBMSにある「limit」の代わりに使うことで、取得するレコード件数を制限することが可能です。

例えば、以下のSQLは、employees(社員)テーブルから、ROWNUMが11未満のデータ、つまり10件のみデータを取得します。

SELECT *
  FROM employees
  WHERE ROWNUM < 11;

関連)Oracle公式リファレンス:ROWNUM疑似列

同様に取得したレコードに連番を付与する関数として、ROW_NUMBER()があります。ROW_NUMBERは、PARTITION BYやORDER BYと組み合わせてグルーピングやソートを組み合わせて、グループごとに連番を振るなどより複雑な用途に使われます。

【関連記事】
SQLのrow_number関数の構文 パーティション・ソートとの組み合わせ

OracleのROWNUMの注意点

以下のSQLは、行を返しません。

SELECT *
  FROM employees
  WHERE ROWNUM > 1;

最初の行のROWNUMは1のため、条件は偽となります。2行目のROWNUMも1となるため、条件は偽と判定されます。その後すべての行が偽となるんですね。

感覚的には、2行目以降すべてを抽出する条件として動作しそうなのですが、上記のような結果になります。同様に、ROWNUMを指定して特定の行のみ取り出そうとした場合にも、意図通り動きません。

OracleのROWNUMは、行の途中から結果を抽出できない

以下のSQLは、「2行目のみ取り出す」結果にならず、行を返しません。

SELECT * FROM employees
WHERE ROWNUM = 2;

ROWNUMで行の途中から結果を抽出したい場合は、サブクエリと組み合わせることで実現は可能です。

SELECT
  column1 
FROM
  ( 
    SELECT
      column1
      , ROWNUM AS rn 
    FROM
      hoge
  ) 
WHERE
  rn BETWEEN 10 AND 20

参考)OracleにないLIMITの代わりにROWNUMを使う場合の罠

なお、指定した行数以降を抽出する場合は、Oracle 12以降でOFFSETが実装されたため、そちらを使用するのが良いでしょう。以下の例は、usersテーブルから、age(年齢)が30以上のレコードを年齢の昇順にソートして10件目以降を抽出します。

SELECT * FROM users 
WHERE age > 30 
ORDER BY age 
OFFSET 10;

関連)Oracle公式リファレンス:OFFSET句

ROWNUMの連番付与後にorder byによるソートが行われる点に注意

ROWNUMとorder byを組合わて使った場合、ROWNUMの連番が振られたあとに、order byによるソートが実行されます。

SELECT 
  ROWNUM 
, Name
, Age
FROM users
ORDER BY Age DESC

| ROWNUM | Name          | Age  |
| ------ | ------------- | ---- |
| 4      | Tanaka Jiro   | 50   |
| 5      | Ito Hanako    | 40   |
| 2      | Suzuki Ichiro | 30   |
| 3      | Sato Taro     | 20   |
| 1      | Yamada Taro   | 10   |

上記のように、ROWNUMがバラバラになってしまうんですね。意図通りにソートを行なうには、ROWNUMではなく、ROW_NUMBER分析関数を使用します。

ROWNUMは指定件数以下に抽出数を絞るなどシンプルな使用にとどめ、複雑なSQLにはROW_NUMBERを使用するのが良さそうです。

ROWNUMが遅くなるケース

ROWNUMを使ったviewにselect文を実行すると、処理が遅くなるというケースがあるようです。viewの定義部分のselect分は以下の通り。

select
    t1.col1
  , t2.col2
  , rownum as col3
from
    table1 t1
    inner join table2 t2
      on t1.pk = t2.pk;

参考)SELECT句にrownumを指定するとフルスキャンとなる場合がある – Qiita

2016年時点(Oracle11)では、ROWNUMは、結果セットすべてに連番を付与するためにテーブルフルスキャンが発生していたため、処理が遅くなっていたようです。

viewで結果セットにROWNUMを付与し、さらにselect文でviewを抽出する入れ子構造になっています。rownumにはindexを付与できないため、データ件数によっては遅くなるんですね。

最新のOracleでは、オプティマイザによって最適化される可能性もあります。入れ子構造で使用する際には、実行計画を確認しましょう。

ROWNUM=1で、データの存在チェックを行なう方法

データの存在チェックをおこなう場合に、余計なテーブルアクセスを発生させないためにROWNUM=1を指定するケースがあります。対象テーブルに何百万件格納されていても、アクセスは一件のみとなるんですね。

SELECT COUNT(*) INTO ln_count
FROM tab_a
WHERE ROWNUM = 1;  --←ここを追加
--
IF ln_count = 0 THEN
  dbms_output.put_line('ERROR!');
  RAISE NO_DATA_FOUND;
END IF;

関連)Tips1. Oracleのパフォーマンス問題~存在チェックが遅い~ 

対象テーブルのデータサイズが大きくて、存在チェックに時間がかかるようなら試してみても良いでしょう。

まとめ

ポテパンダの一言メモ

[\comment]

エンジニアになりたい人に選ばれるプログラミングスクール「ポテパンキャンプ 」

ポテパンキャンプは卒業生の多くがWebエンジニアとして活躍している実践型プログラミングスクールです。 1000名以上が受講しており、その多くが上場企業、ベンチャー企業のWebエンジニアとして活躍しています。

基礎的な学習だけで満足せず、実際にプログラミングを覚えて実践で使えるレベルまで学習したいという方に人気です。 プログラミングを学習し実践で使うには様々な要素が必要です。

それがマルっと詰まっているポテパンキャンプでプログラミングを学習してみませんか?

卒業生の多くがWebエンジニアとして活躍

卒業生の多くがWeb企業で活躍しております。
実践的なカリキュラムをこなしているからこそ現場でも戦力となっております。
活躍する卒業生のインタビューもございますので是非御覧ください。

経験豊富なエンジニア陣が直接指導

実践的なカリキュラムと経験豊富なエンジニアが直接指導にあたります。
有名企業のエンジニアも多数在籍し品質高いWebアプリケーションを作れるようサポートします。

満足度高くコスパの高いプログラミングスクール「ポテパンキャンプ」

運営する株式会社ポテパンは10,000人以上のエンジニアのキャリアサポートを行ってきております。
そのノウハウを活かして実践的なカリキュラムを随時アップデートしております。

代表の宮崎もプログラミングを覚えサイトを作りポテパンを創業しました。
本気でプログラミングを身につけたいという方にコスパ良く受講していただきたいと思っておりますので、気になる方はぜひスクール詳細をのぞいてくださいませ。