具有多列的基于 MySQL 游标的分页

Posted

技术标签:

【中文标题】具有多列的基于 MySQL 游标的分页【英文标题】:MySQL cursor based pagination with multiple columns 【发布时间】:2016-10-27 06:56:23 【问题描述】:

我有一些表,我想使用基于游标的分页来查询,但它需要申请多个列。

让我们举一个使用 2 列的简化示例 - 我这样获取第一页:

SELECT column_1, column_2
FROM table_name
ORDER BY column_1, column_2
LIMIT 10

得到结果后,我可以根据最后一行获取下一页。假设最后一行是column_1 = 5, column_2 = 8。我想做这样的事情:

SELECT column_1, column_2
FROM table_name
WHERE column_1 > 5 AND column_2 > 8
ORDER BY column_1, column_2
LIMIT 10

但这显然是错误的。它将过滤掉具有column_1 = 5, column_2 = 9 的行(因为column_1 上的过滤器)或具有column_1 = 6, column_2 = 6 的行(因为column_2 上的过滤器)

我可以这样做来避免这个问题:

SELECT column_1, column_2
FROM table_name
WHERE column_1 > 5
OR (column_1 = 5 AND column_2 > 8)
ORDER BY column_1, column_2
LIMIT 10

但是对于超过 2 列,这变得非常麻烦且容易出错...

另外,我的用例包括多种类型的列(INT UNSIGNEDBINARY),但都具有可比性

你有什么建议吗?

谢谢!

【问题讨论】:

不。你被那个复杂的WHERE困住了。但它是有效的。另见mysql.rjweb.org/doc.php/… 【参考方案1】:

如果(不幸命名的)列Column_1 是唯一的,您可以这样做:

 WHERE Column_1 > :last_retrieved_value

从问题来看,Column_1 似乎不是唯一的,但 (Column_1,Column_2) 元组是唯一的。

“下一页”查询的一般形式,按这两列排序,使用这两列的最后检索值,将是...

    (Column1,Column2) > (:lrv_col1,:lrv_col2)

(lrv = 从上一个查询检索到的最后一行保存的值)

要在 MySQL 中编写该条件,我们可以像您展示的那样:

 WHERE t.Column_1 > :lrv_col1
    OR ( t.Column_1 = :lrv_col1 AND t.Column_2 > :lrv_col2 )

或者,我们可以这样写,我更喜欢这样,因为 MySQL 被 OR 条件混淆并使用错误索引的机会要少得多...

 WHERE t.Column_1 >= :lrv_col1
       AND ( t.Column_1 > :lrv_col1 OR t.Column_2 > :lrv_col2 )
 ORDER BY t.Column_1, t.Column_2
 LIMIT n

将其扩展到 列,检查条件...

  (c1,c2,c3) > (:lrv1,:lrv2,:lrv3)

我们就像处理两列的情况一样,首先处理c1,然后像两列一样将其拆分:

 WHERE c1 >= :lrv1
       AND ( c1 > :lrv1 OR ( ... ) )
 ORDER BY c1, c2, c3
 LIMIT n

现在占位符...(之前在c2 上进行检查的地方,实际上又只是两列的另一种情况。我们需要检查:(c2,c3) > (lrv2,lrv3),所以我们可以使用相同的模式:

 WHERE c1 >= :lrv1
       AND ( c1 > :lrv1 OR ( c2 >= :lrv2 
                             AND ( c2 > :lrv2 OR c3 > :lrv3 )
                           )
           )
 ORDER BY c1,c2,c3
 LIMIT n

我同意扩展可能看起来有点混乱。但它确实遵循一个非常规律​​的模式。同样,我们可以在 4 列上表达条件...

 (c1,c2,c3,c4) > (:lrv1,:lrv2,:lrv3,:lrv4)

我们只取三列的内容,我们需要扩展 c3 > :lrv3 以将其替换为 ( c3 >= :lrv3 AND ( c3 > :lrv3 OR c4 > :lrv4 ) )

 WHERE c1 >= :lrv1
       AND ( c1 > :lrv1 OR ( c2 >= :lrv2 
                             AND ( c2 > :lrv2 OR ( c3 >= :lrv3
                                                   AND ( c3 > :lrv3 OR c4 > :lrv4 )
                                                 )
                                 )
                           )
           )
 ORDER BY c1,c2,c3,c4
 LIMIT n

作为对未来读者的帮助,我会评论这个块,并指出意图......

 -- (c1,c2,c3,c4) > (lr1,lr2,lr3,lr4)

如果 MySQL 允许我们这样表达比较就好了。不幸的是,我们必须将其扩展为 MySQL 可以理解的内容。

【讨论】:

感谢您的详细解答!我尝试使用简单的“元组”比较并且它有效(MySQL 5.6)。尝试运行一些EXPLAIN 查询,看看它是否比扩展版本更好...... 哈!我不知道 MySQL 支持这种元组比较语法!那会比扩展容易得多...(c1,c2,c3) > (r1,r2,r3)。是的,检查 EXPLAIN 计划...我们希望 MySQL 使用索引范围扫描操作,以 c1,c2,c3 作为前导列。 @spencer7593 - 这要容易得多,但它很慢,因为优化器不知道它。 (8.0 可能已修复此问题。) 复杂的替代方案通过将>< 运算符一起使用,增加了混合ASCDESC 的灵活性。 元组比较 (a,b,c) > (x,z,y) 语法的名称是什么?我在 Postgres 文档中没有找到它。

以上是关于具有多列的基于 MySQL 游标的分页的主要内容,如果未能解决你的问题,请参考以下文章

在使用 graphql 缓存的基于游标的分页中跟踪页码

使用基于 GraphQL 游标的分页避免代码重复

基于游标的分页接口实现

Mybatis分页-利用Mybatis Generator插件生成基于数据库方言的分页语句,统计记录总数 (转)

在mysql 数据库下,基于sql 语言的分页语句

如何从数据库中获取 Graphql 中的分页游标?