10

使用を検討している 2 つのページング クエリがあります。

最初のものは

SELECT * FROM ( SELECT rownum rnum, a.* from (
    select * from members
) a WHERE rownum <= #paging.endRow# ) where rnum > #paging.startRow#

そして2つ目は

SELECT * FROM ( SELECT rownum rnum, a.* from (
    select * from members
) a ) WHERE rnum BETWEEN #paging.startRow# AND #paging.endRow#

どのクエリが速いと思いますか?

4

3 に答える 3

15

現在、実際に Oracle を利用できるわけではありませんが、ページングに最適な SQL クエリは次のとおりです。

select *
from (
        select rownum as rn, a.*
        from (
                select *
                from my_table
                order by ....a_unique_criteria...
            ) a
    )
where rownum <= :size
    and rn >  (:page-1)*:size

http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html

一貫したページングを実現するには、一意の基準を使用して行を並べ替える必要があります。そうすることで、ページ Y ( !=X ) に既にロードされている行をページ X にロードすることを回避できます。

編集:

1) 一意の基準を使用して行を並べ替えるとは、クエリを実行するたびに各行が同じ位置を維持するようにデータを並べ替えるという意味です。

2) ORDER BY 句で使用されるすべての式を含むインデックスは、特に最初のページで結果をより速く取得するのに役立ちます。そのインデックスを使用すると、オプティマイザーによって選択された実行プランは行をソートする必要がありません。これは、インデックスを自然な順序でスクロールして行を返すためです。

3) ところで、クエリの結果をページングする最も速い方法は、クエリを 1 回だけ実行し、アプリケーション側からすべての流れを処理することです。

于 2012-07-27T17:35:27.977 に答える
9

1000 行の例の実行計画を見てみましょう。

SELECT *
  FROM (SELECT ROWNUM rnum
              ,a.*
          FROM (SELECT *
                  FROM members) a
         WHERE ROWNUM <= endrow#)
 WHERE rnum > startrow#;

--------------------------------------------------------------------------------
| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |         |  1000 | 39000 |     3   (0)| 00:00:01 |
|*  1 |  VIEW                |         |  1000 | 39000 |     3   (0)| 00:00:01 |
|   2 |   COUNT              |         |       |       |            |          |
|*  3 |    FILTER            |         |       |       |            |          |
|   4 |     TABLE ACCESS FULL| MEMBERS |  1000 | 26000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RNUM">"STARTROW#")
   3 - filter("MEMBERS"."ENDROW#">=ROWNUM)

そして2。

SELECT *
  FROM (SELECT ROWNUM rnum
              ,a.*
          FROM (SELECT *
                  FROM members) a)
 WHERE rnum BETWEEN startrow# AND endrow#;

-------------------------------------------------------------------------------
| Id  | Operation           | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |         |  1000 | 39000 |     3   (0)| 00:00:01 |
|*  1 |  VIEW               |         |  1000 | 39000 |     3   (0)| 00:00:01 |
|   2 |   COUNT             |         |       |       |            |          |
|   3 |    TABLE ACCESS FULL| MEMBERS |  1000 | 26000 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RNUM"<="ENDROW#" AND "RNUM">="STARTROW#")

そのため、バージョン 2はステップが 1 つ少ないため、わずかに高速になる可能性があります。しかし、あなたのインデックスとデータの分布についてはわかりません。これらの実行計画を自分で取得し、データの状況を判断するのはあなた次第です。または、単にテストします。

于 2012-07-27T05:39:36.627 に答える
0

ここではすでに回答済みですが、コピペさせてください。

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

Oracle 12c より前は OFFSET/FETCH 機能がなかったため、@jasonk が提案したホワイトペーパーを参照してください。これは、長所と短所の詳細な説明を含むさまざまな方法について私が見つけた最も完全な記事です。ここにコピペするとかなりの時間がかかるので、やりたいです。

また、jooq の作成者による、Oracle やその他のデータベースのページネーションに関する一般的な注意事項を説明した優れた記事もあります。jooqのブログ投稿

良いニュースです。Oracle 12c 以降、新しい OFFSET/FETCH 機能が追加されました。OracleMagazine 12c の新機能。「トップ N クエリとページネーション」を参照してください。

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

SELECT * FROM V$VERSION
于 2015-11-03T11:37:24.193 に答える