mysql - 基于光标的分页没有偏移?
问题描述
对于大型数据集,offset
基于分页会变得很慢,因此更快的方法是使用基于游标的分页。基本上,一个锚点,数据库知道从该点开始查找结果。考虑到这一点,这是我面临的问题:
我有tv_watchers
一个自动递增的表id
,mins_watching_tv
, 和user_id
(下面总共 20 行小提琴)。在这个例子user_id
中也是一样的1
,所以不用担心。我们想按照看电视的分钟数从高到低排序。
使用此查询很容易做到这一点:
SELECT * FROM tv_watchers
ORDER BY mins_watching_tv DESC, id ASC
这将返回 20 个字段的正确顺序,按照我们希望的方式按 id 排序:
2, 17, 1, 16, 15, 5, 6, 7, 8, 9, 10, 11, 12, 13, 20, 3, 4, 14, 19, 18
问题是我们想把它分成 5 个块(我们称之为批次),因为我们想按上面的顺序返回 5 个结果。我们通过检索前 6 个结果,将前 5 个结果返回给用户,并使用第 6 个(如果存在)作为光标(锚点)来获取下一批结果:这将正确返回第一批:
-- (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
of 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
of 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
但问题是返回的结果不正确,它返回上面评论中看到的不正确的批次 3 id。我确信它与该WHERE
部分有关,它似乎拾取了该mins_watching_tv <= 60
部分,但该id=10
部分在那里让数据库知道从 60 分钟和 id 10 的锚点获取结果,但这不能正常工作。
最终的批处理结果应如下所示:
-- (Batch 4) 3, 4, 14, 19, 18
我在这里设置了一个sql fiddle来显示这个问题。我们如何修复查询,使其尊重游标组合以及mins_watching_tv
批量id
返回正确结果?
解决方案
像你已经做的那样选择你的前 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
. - 如果
mins_watching_tv = @duration
我们还不知道我们是否已经拥有该行。但是当我们另外做了一个ORDER BY id ASC
时,我们知道我们已经拥有的所有行mins_watching_tv
的 id 都小于或等于当前最大值@id
(permins_watching_tv
)。所以我们只想要那些行,id > @id
或者,因为我们还希望上一个结果的最后一行重复,id = @id
。总之就是id >= @id
。
由于我们想要这两个集合的并集,我们必须分离上述谓词,所以使用OR
. 我们得到(括号只是为了清楚起见,不需要它们):
(mins_watching_tv < @duration)
OR (mins_watching_tv = @duration
AND id >= @id)
这是小提琴。
推荐阅读
- reactjs - React Hooks Exhaustive Deps 建议使用无限循环
- azure-functions - azure-functions-maven-plugin 问题:请降级项目编译版本并重试
- node.js - 如何在对话流中使用重复关键字?
- java - java中关于copyEven的问题
- python - 无法在 tkinter 中打印输入
- java - 如何从java执行存储过程?
- ipad - 如何更改 Ipad 激光雷达的分辨率
- laravel - 生成多条记录,指定从 Laravel 中的 Tinker 调用工厂的值
- python - 根据来自另一个数据帧的值查找数据帧值的平均值
- flutter - 使用带有flutter_bloc的Equatable类