【问题标题】:Generic SQL predicate to use for keyset pagination on multiple fields用于多个字段的键集分页的通用 SQL 谓词
【发布时间】:2019-11-05 06:13:44
【问题描述】:

高性能分页的常见解决方案是使用索引字段,从前一页的最后一个值开始每个新“页”。例如,对于这样的数据集(假设 Category 和 ID 是主键):

Category | ID | Name
Red      | 10 | Bob Jones
Red      | 14 | Sam Smith
Red      | 16 | Jill White
Blue     | 10 | Mike Green
Blue     | 16 | Mary Brown

假设(相当小的)页面大小为 1,如果我们想要返回所有 Red 类别记录(假设 ORDER BY Category, ID):

SELECT * FROM table WHERE Category='Red' AND ID>'00' (1st page, returns Bob Jones)
SELECT * FROM table WHERE Category='Red' AND ID>'10' (2nd page, returns Sam Smith)
SELECT * FROM table WHERE Category='Red' AND ID>'14' (3rd page, returns Jill White)

这是可行的,因为通过分页,“keyset”仅使用 ID 字段(如果 ID 是全局唯一的,它也可以在多个字段上工作,但事实并非如此)。

但是如果我想返回所有的 Red 和 Blue 记录(假设该表还包含其他 Categories),仍然是一次一页(假设 ORDER BY Category, ID):

SELECT * FROM table WHERE Category IN ['Red', 'Blue'] AND Category>'' AND ID>'00' (1st page, returns Bob Jones)
SELECT * FROM table WHERE Category IN ['Red', 'Blue'] AND Category>'Red' AND ID>'10' (2nd page, returns Sam Smith, but skips Mike Green)

在 PostgreSQL 和其他一些中,有一个“行值”谓词语法支持这一点(假设 ORDER BY Category, ID):

SELECT * FROM table WHERE (Category, ID) > ('', '00') (1st page, returns Bob Jones)
SELECT * FROM table WHERE (Category, ID) > ('Red', '10') (2nd page, returns Sam Smith)

之所以有效,是因为 Category 和 ID 在测试中都被视为单个复合值。但我没有使用 PostgreSQL 或支持“行值”的数据库。所以问题是是否有替代解决方案可以解决这个问题(是否有 2 个或 n 个字段)?为了让它在多个变量字段上进行分页,我需要设置一个谓词,该谓词将始终在多字段排序顺序中找到“下一条记录”。

PS:OFFSET/LIMIT 或 SKIP/LIMIT 分页当然可以,但在大型数据集上都没有效率,这就是我尝试使用“keyset”分页的原因。

【问题讨论】:

  • 使用更复杂的谓词可以实现键集分页,但它不会像 PostgreSQL 那样高效(在索引使用方面),因为有几个数据库没有实现“元组不等式”操作员。不过,DB2、PostgreSQL、MariaDB、MySQL、H2 和 HyperSQL 确实实现了它。您使用的是哪个数据库?
  • MSSQL 和 OrientDB

标签: sql sql-server pagination orientdb keyset-pagination


【解决方案1】:

您总是可以对谓词进行短语化:

(x, y) > (a, b)

作为:

x >= a and (x = a and y > b or x > a)

注意第一个谓词x >= a 促进(它不确保)该列上索引的使用。也就是说,它变成了一个“访问谓词”。第二个x = a and y > b or x > a 过滤掉多余的行,有效地成为“过滤谓词”。

这种表述“元组不等式”谓词的方式促进了索引的使用。但是,如果您比较 3、4 或更多列,它们会变得越来越复杂。

【讨论】:

  • 是的,我想这在技术上是正确的。我玩过这种方法,但正如你所说,超过两列它变得相当复杂。但是“访问谓词”是新奇的——我将不得不使用它,看看查询优化器是如何处理它的。
  • 这样拆分还允许两列按相反方向排序的情况。 x <= a AND (x < a OR (x = a AND y > b))
【解决方案2】:

The Impaler's answer 上扩展,使用复合键的键集分页的通用语法如下:

WHERE
  (x > a) OR
  (x = a AND y > b) OR
  (x = a AND y = b AND z > c) OR
  ...

这不如(x, y, z) > (a, b, c) 好,但您可以使用您选择的语言生成 SQL。您遍历这组复合字段并扩展每个连续字段以包含先前字段的{field} = {value} AND

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-31
    • 2020-05-20
    • 2017-10-03
    • 2012-05-19
    相关资源
    最近更新 更多