2017-06-13 84 views
0

我使用postgres,並試圖分頁結果。 這兩個標準的方法未能滿足我的需求,這是分頁能力跳躍非相鄰頁面無偏移限制

  1. 抵免限額 - 然後我來到這裏是因爲無法跳上非相鄰頁

的 - 因爲成本高抵消

  • 尋求的跨越ROW_NUMBER()方法。代碼如下

    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 
    

    我不理解這是如何工作的,如果我有一個讓我的結果它仍然會通過整個數據庫搜索,然後將它們分配唯一的行號WHERE條件,然後選擇的一定範圍內row_numbers。 那麼它是如何比偏移限制更好?。有人可以解釋它的工作和表現嗎?

  • +1

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

    +0

    順便說一句你想在Postgres(在標籤中提到)還是在MS SQL中(問題中的代碼)? – Abelisto

    +0

    我想在postgres中,我可以找到它的代碼,以便張貼mySql代碼以供參考 – madhur

    回答

    2

    上面引用的代碼實際上比使用LIMITOFFSET差,因爲它本質上手工完成了相同的事情,而且代碼很複雜,PostgreSQL不會發現它可以使用部分索引掃描或一個top-N heapsort

    我會告訴你一個例子:

    \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不必排序,但它仍然掃描整個子查詢結果並拋出大部分行。