使用PostgreSQL 10.5。我正在尝试创建一个分页系统,用户可以在各种结果之间来回切换。
为了避免使用OFFSET
,我将上一页中最后一行的id
作为参数p
(prevId)传递。然后,我选择第一个id
大于p
参数值的三行数据。(如this article所述)
例如,如果上一页中最后一行的id
是5,我会选择id
大于5的前3行:
SELECT
id,
firstname,
lastname
FROM
people
WHERE
firstname = 'John'
AND id > 5
ORDER BY
ID ASC
LIMIT
3;
这个很好用,而且时间也不算太糟糕。
Limit (cost=0.00..3.37 rows=3 width=17) (actual time=0.046..0.117 rows=3 loops=1)
-> Seq Scan on people (cost=0.00..4494.15 rows=4000 width=17) (actual time=0.044..0.114 rows=3 loops=1)
Filter: ((id > 5) AND (firstname = 'John'::text))
Rows Removed by Filter: 384
Planning time: 0.148 ms
Execution time: 0.147 ms
然而,如果用户想要返回到上一页,情况就有些不同:
首先,我会传递第一行的id
,然后在其前面加上减号,以表示我应该选择id
小于(一个正数)p
参数的行。换句话说,如果第一行的id
是6,那么p
参数将为-6
。类似地,我的查询将如下所示:
SELECT
*
FROM
(
SELECT
id,
firstname,
lastname
FROM
people
WHERE
firstname = 'John'
AND id < 6
ORDER BY
id DESC
LIMIT
3
) as d
ORDER BY
id ASC;
在上面的查询中,我首先选择了最后3行,这些行的id
小于6,然后将它们反转,以便以与开始时描述的第一个查询相同的方式呈现。
这样做是有效的,但由于数据库需要遍历几乎所有的行,性能受到了影响。
Sort (cost=4252.75..4252.76 rows=1 width=17) (actual time=194.464..194.464 rows=0 loops=1)
Sort Key: people.id
Sort Method: quicksort Memory: 25kB
-> Limit (cost=4252.73..4252.73 rows=1 width=17) (actual time=194.460..194.460 rows=0 loops=1)
-> Sort (cost=4252.73..4252.73 rows=1 width=17) (actual time=194.459..194.459 rows=0 loops=1)
Sort Key: people.id DESC
Sort Method: quicksort Memory: 25kB
-> Gather (cost=1000.00..4252.72 rows=1 width=17) (actual time=194.448..212.010 rows=0 loops=1)
Workers Planned: 1
Workers Launched: 1
-> Parallel Seq Scan on people (cost=0.00..3252.62 rows=1 width=17) (actual time=18.132..18.132 rows=0 loops=2)
Filter: ((id < 13) AND (firstname = 'John'::text))
Rows Removed by Filter: 100505
Planning time: 0.116 ms
Execution time: 212.057 ms
尽管如此,我很感激您花时间阅读到这里,我的问题是:如何使分页更有效率?