2

Web サイトでページングを行う必要がある場合... どちらの方法がより効果的ですか?

分析関数 - ROW_NUMBER()

ROWNUM

  • http://www.oracle.com/technetwork/issue-archive/2007/07-jan/o56asktom-086197.html
  • INMHO このアプローチは、より人間が読めるコードだと思います

    SELECT * FROM (
      SELECT rownum rn, a.* 
      FROM(
        SELECT columnA, columnB
        FROM table 
        ORDER BY columnB
      ) a 
      WHERE rn <= OFFSET
    )
    WHERE rnum >= LOW_LIMIT
    
    • 注: RANK および DENSE_RANK 分析関数があることは理解していますが、決定論的クエリをページングするだけでよいと仮定しましょう。

    • 注2:別の単純なクエリカウントを使用して考えているレコードの合計量を取得するには(*)

4

3 に答える 3

7

この質問は面白いと思ったので、いくつか試してみました。

約 1.1M 行を含む large_t というテーブルがあります。

次に、2 つのクエリがあります。

select * 
from
(
  select rownum rnum, a.*
  from (
         select owner, object_name, object_id
         from large_t
         order by object_id
       ) a
  where rownum   <= 30      
) where rnum > 20;

select *
from
(
select owner, object_name, object_id,
       row_number() over (order by object_id) rnum
from large_t
) where rnum > 20 and rnum <= 30;

2 つのクエリが生成するプランを見ると、最初のクエリには操作があります。

SORT ORDER BY STOPKEY

分析クエリには、と呼ばれる操作が含まれていますが、

WINDOW SORT PUSHED RANK

SORT ORDER BY STOPKEY は、単純な ORDER BY よりも効率的な並べ替え操作です。WINDOW SORT PUSHED RANK がどのように機能するかはわかりませんが、同様の方法で機能するようです。

両方のクエリを実行した後に v$sql_workarea を見ると、どちらも 4096 バイトの sort_area しか必要としませんでした。

対照的に、ページング クエリを使用せずにクエリを実行すると、次のようになります。

select owner, object_name, object_id
from large_t
order by object_id

次に、必要なソート領域は 37M であり、両方のクエリのソートがほぼ同じであることを証明しています。

通常、ソートされたクエリのTOP Nを効率的に返したい場合は、ソート列にインデックスが必要になります。これにより、Oracleがソートする必要がまったくなくなります。そこで、OBJECT_ID にインデックスを作成し、両方のクエリについて再度説明しました。

今回は、最初のクエリはインデックスを使用し、0.2 秒で返されましたが、2 番目のクエリは新しいインデックスを使用しなかったため、はるかに遅くなりました。

したがって、この簡単な分析から得た私の結論は、一般的なケースでは、rownum を使用してフィルター処理を行うか、分析的な row_number 関数を使用すると、どちらもほぼ同じように機能するということです。ただし、rownum の例では、row_number がテーブルに作成しなかったときに、テーブルに作成したインデックスを使用して自動的に開始されました。たぶん、いくつかのヒントでインデックスを使用できるようにすることができます-それはあなたが実験できる別のものです.

于 2011-08-16T10:43:24.413 に答える
4

回答に記載されている他の違いとは別に、パフォーマンスも考慮する必要があります。ここには、権威はありませんが非常に興味深いレポートがあり、ページネーションのさまざまな手段をROWNUM比較していROW_NUMBER() OVER()ます。

https://web.archive.org/web/20160901191310/http://www.inf.unideb.hu:80/~gabora/pagination/results.html

于 2012-07-18T09:48:41.427 に答える
0

独自の実験結果を生成するには:

-- Create test table
CREATE TABLE test_large_tab (
  tlt_id   NUMBER,
  tlt_data VARCHAR2(50)
);

-- Load with data
BEGIN
   FORALL i IN 1 .. 1000000
      INSERT INTO test_large_tab
      (
       tlt_id,
       tlt_data
      )
      VALUES
      (
       i,
       TO_CHAR(sysdate-i, 'FMMon ddth, YYYY')
      );
END;

もちろん、テストの目的に合わせてテーブルのサイズを大きくすることもできます!

タイミングをオンに設定し、大きなテーブルに対して両方のクエリを実行します。

いくつかの列にクエリのインデックスを付けたい場合など、テストに合わせてテーブル構造を変更しますが、基本的には単純なテストであり、実行に時間がかかりません。

両方ともほぼ同じタイミングである場合は、最も読みやすい (したがってサポート可能な) バージョンを使用します。

于 2011-08-16T09:59:01.093 に答える