Oracleによるページング


97

私はOracleに精通していません。25万件のレコードがあり、ページごとに100件表示したいと考えています。現在、データアダプターとデータセットを使用して、25万レコードすべてをデータセットに取得する1つのストアドプロシージャと、ストアドプロシージャの結果のdataadapter.Fill(dataset)メソッドがあります。パラメータとして渡すことができる整数値として「ページ番号」と「ページごとのレコード数」がある場合、その特定のセクションだけを取得するための最良の方法は何ですか。たとえば、selectステートメントからページ番号として10を渡し、ページ数として120を渡すと、1880番目から1200番目までのようになり、私の頭の中の数学はうまくいかないかもしれません。

私はこれをC#を使用して.NETで行っていますが、それは重要ではないと思いました。SQL側で正しく取得できれば、クールなはずです。

更新:私はブライアンの提案を使用することができました、そしてそれは素晴らしい働きをしています。いくつかの最適化に取り組みたいのですが、ページは1分ではなく4〜5秒で表示され、ページングコントロールは新しいストアドプロシージャと非常にうまく統合できました。

回答:


144

このようなものが機能するはずです:Frans Boumaのブログから

SELECT * FROM
(
    SELECT a.*, rownum r__
    FROM
    (
        SELECT * FROM ORDERS WHERE CustomerID LIKE 'A%'
        ORDER BY OrderDate DESC, ShippingDate DESC
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE r__ >= (((pageNumber-1) * pageSize) + 1)

4
はい、それはOracleがサポートする「組み込み」列であり、常に1から始まり、各行ごとに増加します。したがって、このコードスニペットでは、1000行ある場合、並べ替え順序が適用され、各行にrownumが割り当てられます。外側の選択は、それらの行番号を使用して、ページサイズに基づいて探している「ページ」を見つけます。
ブライアンシュミット

9
これはいいですが、大規模な選択ではひどく遅くなります。0〜1000と500.000〜501.000を選択する時間を確認してください...この種の選択構造を使用していたので、回避策を探しています。
newhouse

3
@ n3whous3あなたはこれを試すかもしれません-inf.unideb.hu/~gabora/pagination/results.html
jasonk

7
なぜ2つWHEREをと組み合わせられないのか疑問に思い、AND次のコードを
Mengdi Gao

1
オラクルのページネーションは私の日を台無しにします。
Aetherus

134

トムにページネーションと非常に便利な分析機能について尋ねます。

これはそのページからの抜粋です。

select * from (
    select /*+ first_rows(25) */
     object_id,object_name,
     row_number() over
    (order by object_id) rn
        from all_objects)
    where rn between :n and :m
        order by rn;

7
これは実際にははるかに優れた実装ですが、その投稿では見つけるのは困難です。大きなページがたくさんある場合、他の答えは前のページのすべての行にも適用する必要があります。複雑なクエリでは、これは後のページが前のページよりもパフォーマンスが悪いことを意味します。
tallseth

@tallsethそうですね。そのページで見つけるのは難しいです。抜粋を追加しました。
Chobicus

これは、注文を動的に変更したい場合の正解です。
chakeda

74

完全を期すために、より最新のソリューションを探している人のために、Oracle 12cには、より優れたページングやトップハンドリングなど、いくつかの新機能があります

ページング

ページングは​​次のようになります。

SELECT *
FROM user
ORDER BY first_name
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY;

トップNレコード

トップレコードの取得は次のようになります。

SELECT *
FROM user
ORDER BY first_name
FETCH FIRST 5 ROWS ONLY

上記のクエリ例の両方にORDER BY句があることに注意してください。新しいコマンドはこれらを尊重し、ソートされたデータに対して実行されます。

FETCHまたはに適したOracleリファレンスページを見つけることができませんでしたOFFSETが、このページには、これらの新機能の優れた概要があります。

パフォーマンス

@wweickerが以下のコメントで指摘しているように、パフォーマンスは12cの新しい構文の問題です。18cのコピーを持っていなかったので、Oracleによって改善されたかどうかをテストしました。

興味深いことに、新しいメソッドのテーブル(1億1300万行以上)に対して初めてクエリを実行したとき、実際の結果は少し速く返されました。

  • 新しい方法:0.013秒。
  • 古い方法:0.107秒。

ただし、@ wweickerが述べたように、新しい方法では説明プランがはるかに悪く見えます。

  • 新しいメソッドのコスト:300,110
  • 古い方法のコスト:30

新しい構文により、列のインデックスが完全にスキャンされましたが、これが全体のコストでした。おそらく、インデックス付けされていないデータを制限すると事態はさらに悪化します。

前のデータセットに単一のインデックス付けされていない列を含める場合を見てみましょう。

  • 新しいメソッドの時間/コスト:189.55秒/ 998,908
  • 古いメソッドの時間/コスト:1.973秒/ 256

概要:Oracleがこの処理を改善するまで注意して使用してください。使用するインデックスがある場合は、新しいメソッドを使用することで問題を回避できる可能性があります。

うまくいけば、私はすぐに遊ぶことができる18cのコピーを手に入れ、更新することができます


これは12cユーザーにとって素晴らしい回答です
Lalji Gajera

1
構文はきれいですが、パフォーマンスが悪化し(あるdba-presents.com/index.php/databases/oracle/...
wweicker

知っておくと便利です、@ wweickerに感謝します。うまくいけば、パフォーマンスはOracleによってすぐに修正されます。ただし、Oracleを知っていれば、これは遠い希望かもしれません。
JoelC

構文は新しく、通常のROW_NUMBER / RANK呼び出しに変換されます。関連注文後にOracleクエリによって返される行数を制限するにはどうすればよいですか?
Lukasz Szozda

@JoelCあなたの意見に変化はありましたか?
ライアン

11

回答とコメントを要約したいだけです。ページネーションを行う方法はいくつかあります。

Oracle 12cより前のバージョンでは、OFFSET / FETCH機能はありませんでした。@ jasonkが提案したホワイトペーパーをご覧ください。これは、さまざまな方法について私が見つけた最も完全な記事であり、長所と短所について詳しく説明しています。ここにコピーして貼り付けるにはかなりの時間がかかるので、ここでは行いません。

また、Oracleおよびその他のデータベースのページ分割に関するいくつかの一般的な注意点を説明するjooq作成者による優れた記事もあります。jooqのブログ投稿

Oracle 12c以降、新しいオフセット/フェッチ機能が追加されました。OracleMagazine 12cの新機能。「トップNクエリとページネーション」を参照してください。

次のステートメントを発行して、Oracleのバージョンを確認できます。

SELECT * FROM V$VERSION

7

以下を試してください:

SELECT *
FROM
  (SELECT FIELDA,
    FIELDB,
    FIELDC,
    ROW_NUMBER() OVER (ORDER BY FIELDC) R
  FROM TABLE_NAME
  WHERE FIELDA = 10
  )
WHERE R >= 10
AND R   <= 15;

[tecnicume]経由


0

私のプロジェクトでは、Oracle 12cとjavaを使用しました。ページングコードは次のようになります。

 public public List<Map<String, Object>> getAllProductOfferWithPagination(int pageNo, int pageElementSize, Long productOfferId, String productOfferName) {
    try {

        if(pageNo==1){
            //do nothing
        } else{
            pageNo=(pageNo-1)*pageElementSize+1;
        }
        System.out.println("algo pageNo: " + pageNo +"  pageElementSize: "+ pageElementSize+"  productOfferId: "+ productOfferId+"  productOfferName: "+ productOfferName);

        String sql = "SELECT * FROM ( SELECT * FROM product_offer po WHERE po.deleted=0 AND (po.product_offer_id=? OR po.product_offer_name LIKE ? )" +
             " ORDER BY po.PRODUCT_OFFER_ID asc) foo OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ";

       return jdbcTemplate.queryForList(sql,new Object[] {productOfferId,"%"+productOfferName+"%",pageNo-1, pageElementSize});

    } catch (Exception e) {
        System.out.println(e);
        e.printStackTrace();
        return null;
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.