没有偏移量的基于游标的分页?
Cursor based pagination without offset?
对于大型数据集,offset
基于分页会变得很慢,因此一种更快的方法是使用基于游标的分页。基本上,一个锚点,数据库知道从该点开始查找结果。考虑到这一点,这是我面临的问题:
我有一个 tabletv_watchers
,带有自动递增的 id
、mins_watching_tv
和 user_id
(下面总共 20 行 fiddle) .在本例中 user_id
将与 1
相同,因此无需担心。我们想按看电视的分钟数从高到低排序。
使用这个查询很容易完成:
SELECT * FROM tv_watchers
ORDER BY mins_watching_tv DESC, id ASC
这将 return 20 个字段的正确顺序按照我们希望的方式按 id:
排序
2, 17, 1, 16, 15, 5, 6, 7, 8, 9, 10, 11, 12, 13, 20, 3, 4, 14, 19, 18
问题是我们希望将其分成 5 个块(我们称之为批次),因为我们希望 return 5 个结果按上面的顺序排列。我们通过检索前 6 个结果,return 将前 5 个结果发送给用户,并使用第 6 个(如果它存在)作为光标(锚点)从以下位置获取下一批:
这 return 第一批正确:
-- (Batch 1) 2, 17, 1, 16, 15, 5
SELECT * FROM tv_watchers
ORDER BY mins_watching_tv DESC, id ASC
LIMIT 6
这里的第 6 项是 id 5
,它的 mins_watching_tv
为 60
,因此由于这是游标,我们使用它来获取下一个 6,如下所示:
-- (Batch 2) 5, 6, 7, 8, 9, 10
SELECT * FROM tv_watchers
WHERE mins_watching_tv <= 60 OR id=5
ORDER BY mins_watching_tv DESC, id ASC
LIMIT 6
这里的第 6 项是 id 10
,它也有一个 mins_watching_tv
的 60
,所以因为这是光标,所以我们使用它来获取下一个 6,如下所示:
-- (Batch 3 should be) 10, 11, 12, 13, 20, 3
-- (Batch 3 returns incorrectly) 5, 6, 7, 8, 9, 10
SELECT * FROM tv_watchers
WHERE mins_watching_tv <= 60 OR id=10
ORDER BY mins_watching_tv DESC, id ASC
LIMIT 6
但问题是返回的结果不正确,return是上面评论中看到的不正确的批次 3 ID。我确定它与 WHERE
部分有关,它似乎选择了 mins_watching_tv <= 60
部分,但 id=10
部分是为了让数据库知道从该锚点获取结果60 分钟和 id 10 点,但这不能正常工作。
最终批处理结果应如下所示:
-- (Batch 4) 3, 4, 14, 19, 18
我设置了一个 sql fiddle here 来显示问题。我们如何修复查询,使其尊重 mins_watching_tv
的游标组合与 id
到 return 的批量正确结果?
我只是略读了一下,但我认为您只需要将条件调整为(例如)
mins_watching_tv < 60 OR (mins_watching_tv = 60 AND id>=5)
Select 你的前 6 个就像你已经做的一样,WHERE
.
中没有任何内容
SELECT *
FROM tv_watchers
ORDER BY mins_watching_tv DESC,
id ASC
LIMIT 6;
持续时间@duration
和上一步结果最后一行的ID@id
放入WHERE
like
SELECT *
FROM tv_watchers
WHERE mins_watching_tv < @duration
OR mins_watching_tv = @duration
AND id >= @id
ORDER BY mins_watching_tv DESC,
id ASC
LIMIT 6;
重复 2. 直到结束。
解释:
- 如果
mins_watching_tv < @duration
我们可以确定相应的行不在我们之前的结果中,因为 mins_watching_tv
小于我们之前结果的最小值 @duration
并且我们做到了ORDER BY mins_watching_tv DESC
.
- If
mins_watching_tv = @duration
我们还不知道我们是否已经有了该行。但是因为我们另外做了一个 ORDER BY id ASC
,我们知道我们已经拥有相同 mins_watching_tv
的所有行都有一个小于或等于当前最大值 @id
的 id(每个 mins_watching_tv
).所以我们只想要 id > @id
的那些行,或者,因为我们还希望重复上一个结果的最后一行,id = @id
。简而言之就是 id >= @id
.
因为我们想要这两个集合的并集,我们必须分离上面的谓词,所以使用 OR
。我们得到(括号只是为了清楚起见,不需要它们):
(mins_watching_tv < @duration)
OR (mins_watching_tv = @duration
AND id >= @id)
而here就是fiddle。
对于大型数据集,offset
基于分页会变得很慢,因此一种更快的方法是使用基于游标的分页。基本上,一个锚点,数据库知道从该点开始查找结果。考虑到这一点,这是我面临的问题:
我有一个 tabletv_watchers
,带有自动递增的 id
、mins_watching_tv
和 user_id
(下面总共 20 行 fiddle) .在本例中 user_id
将与 1
相同,因此无需担心。我们想按看电视的分钟数从高到低排序。
使用这个查询很容易完成:
SELECT * FROM tv_watchers
ORDER BY mins_watching_tv DESC, id ASC
这将 return 20 个字段的正确顺序按照我们希望的方式按 id:
排序2, 17, 1, 16, 15, 5, 6, 7, 8, 9, 10, 11, 12, 13, 20, 3, 4, 14, 19, 18
问题是我们希望将其分成 5 个块(我们称之为批次),因为我们希望 return 5 个结果按上面的顺序排列。我们通过检索前 6 个结果,return 将前 5 个结果发送给用户,并使用第 6 个(如果它存在)作为光标(锚点)从以下位置获取下一批: 这 return 第一批正确:
-- (Batch 1) 2, 17, 1, 16, 15, 5
SELECT * FROM tv_watchers
ORDER BY mins_watching_tv DESC, id ASC
LIMIT 6
这里的第 6 项是 id 5
,它的 mins_watching_tv
为 60
,因此由于这是游标,我们使用它来获取下一个 6,如下所示:
-- (Batch 2) 5, 6, 7, 8, 9, 10
SELECT * FROM tv_watchers
WHERE mins_watching_tv <= 60 OR id=5
ORDER BY mins_watching_tv DESC, id ASC
LIMIT 6
这里的第 6 项是 id 10
,它也有一个 mins_watching_tv
的 60
,所以因为这是光标,所以我们使用它来获取下一个 6,如下所示:
-- (Batch 3 should be) 10, 11, 12, 13, 20, 3
-- (Batch 3 returns incorrectly) 5, 6, 7, 8, 9, 10
SELECT * FROM tv_watchers
WHERE mins_watching_tv <= 60 OR id=10
ORDER BY mins_watching_tv DESC, id ASC
LIMIT 6
但问题是返回的结果不正确,return是上面评论中看到的不正确的批次 3 ID。我确定它与 WHERE
部分有关,它似乎选择了 mins_watching_tv <= 60
部分,但 id=10
部分是为了让数据库知道从该锚点获取结果60 分钟和 id 10 点,但这不能正常工作。
最终批处理结果应如下所示:
-- (Batch 4) 3, 4, 14, 19, 18
我设置了一个 sql fiddle here 来显示问题。我们如何修复查询,使其尊重 mins_watching_tv
的游标组合与 id
到 return 的批量正确结果?
我只是略读了一下,但我认为您只需要将条件调整为(例如)
mins_watching_tv < 60 OR (mins_watching_tv = 60 AND id>=5)
Select 你的前 6 个就像你已经做的一样,
中没有任何内容WHERE
.SELECT * FROM tv_watchers ORDER BY mins_watching_tv DESC, id ASC LIMIT 6;
持续时间
@duration
和上一步结果最后一行的ID@id
放入WHERE
likeSELECT * FROM tv_watchers WHERE mins_watching_tv < @duration OR mins_watching_tv = @duration AND id >= @id ORDER BY mins_watching_tv DESC, id ASC LIMIT 6;
重复 2. 直到结束。
解释:
- 如果
mins_watching_tv < @duration
我们可以确定相应的行不在我们之前的结果中,因为mins_watching_tv
小于我们之前结果的最小值@duration
并且我们做到了ORDER BY mins_watching_tv DESC
. - If
mins_watching_tv = @duration
我们还不知道我们是否已经有了该行。但是因为我们另外做了一个ORDER BY id ASC
,我们知道我们已经拥有相同mins_watching_tv
的所有行都有一个小于或等于当前最大值@id
的 id(每个mins_watching_tv
).所以我们只想要id > @id
的那些行,或者,因为我们还希望重复上一个结果的最后一行,id = @id
。简而言之就是id >= @id
.
因为我们想要这两个集合的并集,我们必须分离上面的谓词,所以使用 OR
。我们得到(括号只是为了清楚起见,不需要它们):
(mins_watching_tv < @duration)
OR (mins_watching_tv = @duration
AND id >= @id)
而here就是fiddle。