2017-06-13 11 views
0

私はpostgresを使用しており、結果の改ページを試みています。シーク高いオフセット オフセット制限なしで隣接していないページをホッピングする機能を持つページネーション

  • でコストのため - - なぜなら非隣接ページ
  • にジャンプすることができないのそれから私が来た 2つの標準的な方法は、

    1. オフセット制限されている私のニーズに応えることができませんでしたROW_NUMBER()メソッド全体で私はそれはまだ全体のデータベースを検索し、それらに一意の行番号を割り当てます私の結果を取得するためのWHERE条件を持っている場合、これは、どのように機能するかを理解していないのです

      DECLARE @row_per_page INT = 100 
      DECLARE @page_number INT = 2 
      
      SELECT * FROM 
      (SELECT ROW_NUMBER() OVER (ORDER BY [ID]) AS [RowNumber],* 
      FROM table_name) AS T 
      WHERE T.[RowNumber] > (@page_number-1)*@row_per_page AND T.[RowNumber] < @page_number*@row_per_page+1 
      

      以下のようなコードがあり、その後、いくつかの範囲を選択し、 row_numbers。 次に、オフセット制限よりもどのように優れていますか?。誰かがその作業とパフォーマンスを説明できますか?

    +1

    https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/ – Abelisto

    +0

    BTW Postgres(タグに記載されています)やMS SQL (質問のコード)? – Abelisto

    +0

    私はポストグルで欲しい、私はそれのためのコードを見つけることができるように参照用のmySqlコードを掲載 – madhur

    答えて

    2

    上記のコードは、実際には同じことを手作業で行うため、実際にはLIMITOFFSETを使用するよりも悪く、PostgreSQLが部分インデックススキャンを使用できるかトップNヒープート

    私はあなたの例を紹介します:

    \d large 
        Table "laurenz.large" 
    ┌────────┬─────────┬───────────┐ 
    │ Column │ Type │ Modifiers │ 
    ├────────┼─────────┼───────────┤ 
    │ id  │ integer │ not null │ 
    │ val │ text │   │ 
    └────────┴─────────┴───────────┘ 
    Indexes: 
        "large_pkey" PRIMARY KEY, btree (id) 
    

    これはあなたがインデックスかいないかに応じて、行いOFFSETLIMITと何ページングです:インデックスなし

    EXPLAIN (ANALYZE, BUFFERS) 
    SELECT * FROM large 
    ORDER BY val 
    OFFSET 50 LIMIT 10; 
                 QUERY PLAN 
    ------------------------------------------------------------------------------------ 
    Limit (cost=52868.58..52868.60 rows=10 width=37) 
         (actual time=1657.981..1658.001 rows=10 loops=1) 
        Buffers: shared hit=8334 
        -> Sort (cost=52868.45..55368.45 rows=1000000 width=37) 
          (actual time=1657.909..1657.950 rows=60 loops=1) 
         Sort Key: val 
         Sort Method: top-N heapsort Memory: 29kB 
         Buffers: shared hit=8334 
         -> Seq Scan on large (cost=0.00..18334.00 rows=1000000 width=37) 
               (actual time=0.010..721.285 rows=1000000 loops=1) 
           Buffers: shared hit=8334 
    Planning time: 0.078 ms 
    Execution time: 1658.036 ms 
    

    をPostgreSQLはテーブル全体をスキャンする必要がありますが、少なくとも最初の60行しか必要ないことがわかります。

    EXPLAIN (ANALYZE, BUFFERS) 
    SELECT * FROM large 
    ORDER BY id 
    OFFSET 50 LIMIT 10; 
                 QUERY PLAN 
    ----------------------------------------------------------------------------------------- 
    Limit (cost=2.14..2.48 rows=10 width=37) 
         (actual time=0.100..0.121 rows=10 loops=1) 
        Buffers: shared hit=4 
        -> Index Scan using large_pkey on large (cost=0.42..34317.43 rows=1000000 width=37) 
                  (actual time=0.022..0.073 rows=60 loops=1 
         Buffers: shared hit=4 
    Planning time: 0.130 ms 
    Execution time: 0.158 ms 
    

    インデックスを使用すると、50のオフセットがそれほど傷つきませんので、物事はかなり高速です。

    今度はrow_numberを使用して同じことを試してみましょう:

    EXPLAIN (ANALYZE, BUFFERS) 
    SELECT id, val 
        FROM (SELECT id, val, 
           row_number() OVER (ORDER BY val) 
         FROM large 
         ) q 
        WHERE row_number BETWEEN 51 AND 60; 
                 QUERY PLAN 
    ---------------------------------------------------------------------------------------- 
    Subquery Scan on q (cost=172682.84..205182.84 rows=5000 width=37) 
            (actual time=5663.090..10611.557 rows=10 loops=1) 
        Filter: ((q.row_number >= 51) AND (q.row_number <= 60)) 
        Rows Removed by Filter: 999990 
        Buffers: shared hit=8334, temp read=9770 written=9770 
        -> WindowAgg (cost=172682.84..190182.84 rows=1000000 width=45) 
            (actual time=5662.803..9795.077 rows=1000000 loops=1) 
         Buffers: shared hit=8334, temp read=9770 written=9770 
         -> Sort (cost=172682.84..175182.84 rows=1000000 width=37) 
            (actual time=5662.784..8099.025 rows=1000000 loops=1) 
           Sort Key: large.val 
           Sort Method: external merge Disk: 48848kB 
           Buffers: shared hit=8334, temp read=9770 written=9770 
           -> Seq Scan on large (cost=0.00..18334.00 rows=1000000 width=37) 
                 (actual time=0.015..827.945 rows=1000000 loops=1 
            Buffers: shared hit=8334 
    Planning time: 0.175 ms 
    Execution time: 10621.032 ms 
    

    (14行)

    PostgreSQLは全体テーブルをソートし、その結果をスキャンし、必要な行以外のすべてを破棄します。

    EXPLAIN (ANALYZE, BUFFERS) 
    SELECT id, val 
        FROM (SELECT id, val, 
           row_number() OVER (ORDER BY id) 
         FROM large 
         ) q 
        WHERE row_number BETWEEN 51 AND 60; 
                  QUERY PLAN 
    --------------------------------------------------------------------------------------------- 
    Subquery Scan on q (cost=0.42..64317.43 rows=5000 width=37) 
            (actual time=0.319..3411.027 rows=10 loops=1) 
        Filter: ((q.row_number >= 51) AND (q.row_number <= 60)) 
        Rows Removed by Filter: 999990 
        Buffers: shared hit=11069 
        -> WindowAgg (cost=0.42..49317.43 rows=1000000 width=45) 
            (actual time=0.040..2585.197 rows=1000000 loops=1) 
         Buffers: shared hit=11069 
         -> Index Scan using large_pkey on large (cost=0.42..34317.43 rows=1000000 width=37) 
                    (actual time=0.024..895.798 rows=10 
           Buffers: shared hit=11069 
    Planning time: 0.261 ms 
    Execution time: 3411.119 ms 
    

    PostgreSQLはソートする必要はありませんが、それはまだ全体のサブクエリの結果をスキャンし、行のほとんどを離れて投げます。

    関連する問題