上記のコードは、実際には同じことを手作業で行うため、実際にはLIMIT
とOFFSET
を使用するよりも悪く、PostgreSQLが部分インデックススキャンを使用できるかトップNヒープート。
私はあなたの例を紹介します:
\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
BTW Postgres(タグに記載されています)やMS SQL (質問のコード)? – Abelisto
私はポストグルで欲しい、私はそれのためのコードを見つけることができるように参照用のmySqlコードを掲載 – madhur