web-dev-qa-db-ja.com

オラクルのページネーションのベストプラクティス?

問題:行の単一ページの結果セットを返すストアドプロシージャを作成する必要がありますおよび行の総数。

解決策A:2つのストアドプロシージャを作成します。1つは単一ページの結果セットを返し、もう1つはスカラー(合計行)を返します。 Explain Planでは、最初のsprocのコストは9であり、2番目のsprocのコストは3であると述べています。

SELECT  *
FROM    ( SELECT ROW_NUMBER() OVER ( ORDER BY D.ID DESC ) AS RowNum, ...
        ) AS PageResult
WHERE   RowNum >= @from
    AND RowNum < @to
ORDER BY RowNum

SELECT  COUNT(*)
FROM    ...

解決策B:同じTotalRows番号を結果セットのevery行に追加して、すべてを単一のsprocに入れます。このソリューションはハック感がありますが、コストは9であり、1つのsprocのみであるため、このソリューションを使用したいと思います。

SELECT * 
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY D.ID DESC  ) RowNum, COUNT(*) OVER () TotalRows,
WHERE RowNum >= from
        AND RowNum < to
ORDER BY RowNum;

Oracleのページネーションのベストプラクティスはありますか?上記のソリューションのうち、実際に最も使用されているのはどれですか?それらのいずれかが単に間違っていると考えられていますか?私のDBは比較的小さく(10GB未満)います。

Oracle 11gおよび最新のODP.NETとVS2010 SP1およびEntity Framework 4.4を使用しています。 EF 4.4内で動作する最終的なソリューションが必要です。一般にページネーションのためのより良い方法があると思いますが、EFで作業する必要があります。

27
Howie

すでに分析(ROW_NUMBER() OVER ...)を使用している場合、同じパーティションに別の分析関数を追加すると、クエリに無視できるコストが追加されます。

一方、ページネーションを行う方法は他にもたくさんあり、そのうちの1つはrownumを使用します。

SELECT * 
  FROM (SELECT A.*, rownum rn
          FROM (SELECT *
                  FROM your_table
                 ORDER BY col) A
         WHERE rownum <= :Y)
 WHERE rn >= :X

順序付け列に適切なインデックスがある場合、この方法は優れています。この場合、2つのクエリ(1つは行の総数、1つは結果)を使用する方が効率的です。

どちらの方法も適切ですが、一般に行数とページネーションセットの両方が必要な場合は、行を1回クエリするだけなので、分析の使用がより効率的です。

26
Vincent Malgrat

これは役立つかもしれません:

   SELECT * FROM 
     ( SELECT deptno, ename, sal, ROW_NUMBER() OVER (ORDER BY ename) Row_Num FROM emp)
     WHERE Row_Num BETWEEN 5 and 10;
5
Art

Oracle 12Cでは、ページネーションにlimit LIMITOFFSETを使用できます。

例-tabデータ型列DATEに基づいて、ページネーションを使用して降順でデータをフェッチする必要があるテーブルdtがあるとします。

page_size:=5

select * from tab
order by dt desc
OFFSET nvl(page_no-1,1)*page_size ROWS FETCH NEXT page_size ROWS ONLY;

説明:

page_no = 1 page_size = 5

OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY-最初の5行のみを取得

page_no = 2 page_size = 5

OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY-次の5行をフェッチします

等々。

参照ページ-

https://dba-presents.com/index.php/databases/Oracle/31-new-pagination-method-in-Oracle-12c-offset-fetch

https://Oracle-base.com/articles/12c/row-limited-clause-for-top-n-queries-12cr1#paging

2
Tajinder

私も同様の問題に直面しました。上記のソリューションをすべて試してみましたが、パフォーマンスが向上するものはありませんでした。私は数百万のレコードを含むテーブルを持っています。画面に20ページで表示する必要があります。この問題を解決するために以下を行いました。

  1. テーブルに新しい列ROW_NUMBERを追加します。
  2. 列を主キーにするか、一意のインデックスを追加します。
  3. 母集団プログラム(私の場合はInformatica)を使用して、列にrownumを取り込みます。
  4. Betweenステートメントを使用してテーブルからレコードをフェッチします。 (SELECT * FROM TABLE WHERE ROW_NUMBER BETWEEN LOWER_RANGE AND UPPER_RANGE)。

この方法は、巨大なテーブルで無条件のページネーションフェッチを行う必要がある場合に効果的です。

0
Ram Kiran

SQLコードを整理するきれいな方法は、WITHステートメントを使用することです。

縮小版は、結果の合計数および合計ページ数も実装します。

例えば

_WITH SELECTION AS (
    SELECT FIELDA, FIELDB, FIELDC FROM TABLE), 
NUMBERED AS (
    SELECT 
    ROW_NUMBER() OVER (ORDER BY FIELDA) RN, 
    SELECTION.*
    FROM SELECTION)
SELECT
    (SELECT COUNT(*) FROM NUMBERED) TOTAL_ROWS,
    NUMBERED.*
FROM NUMBERED
WHERE 
    RN BETWEEN ((:page_size*:page_number)-:page_size+1) AND (:page_size*:page_number)
_

このコードは、さらに2つのフィールドを持つページ化された結果セットを提供します。

  • _TOTAL_ROWS_完全なSELECTIONの合計行
  • RNレコードの行番号

SELECTIONをスライスするには、2つのパラメーター_:page_size_と_:page_number_が必要です。

縮小版

選択はすでにROW_NUMBER()フィールドを実装しています

_WITH SELECTION AS (
    SELECT 
        ROW_NUMBER() OVER (ORDER BY FIELDA) RN,
        FIELDA, 
        FIELDB, 
        FIELDC 
    FROM TABLE) 
SELECT
    :page_number PAGE_NUMBER,
    CEIL((SELECT COUNT(*) FROM SELECTION ) / :page_size) TOTAL_PAGES,
    :page_size PAGE_SIZE,
    (SELECT COUNT(*) FROM SELECTION ) TOTAL_ROWS,
    SELECTION.*
FROM SELECTION 
WHERE 
    RN BETWEEN ((:page_size*:page_number)-:page_size+1) AND (:page_size*:page_number)
_
0
Stefano Giraldi

申し訳ありませんが、これはソートで動作します:

SELECT * FROM (SELECT ROWNUM rnum,a.* FROM (SELECT * FROM "tabla" order by "column" asc) a) WHERE rnum BETWEEN "firstrange" AND "lastrange";
0
Carlos

これを試して:

select * from ( select * from "table" order by "column" desc ) where ROWNUM > 0 and ROWNUM <= 5;
0
Carlos