2017-06-13 64 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不必排序,但它仍然扫描整个子查询结果并抛出大部分行。