上面引用的代碼實際上比使用LIMIT
和OFFSET
差,因爲它本質上手工完成了相同的事情,而且代碼很複雜,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)
這是尋呼與OFFSET
和LIMIT
做,這取決於你是否有一個索引或不:不帶指數
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不必排序,但它仍然掃描整個子查詢結果並拋出大部分行。
https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/ – Abelisto
順便說一句你想在Postgres(在標籤中提到)還是在MS SQL中(問題中的代碼)? – Abelisto
我想在postgres中,我可以找到它的代碼,以便張貼mySql代碼以供參考 – madhur