用于多个字段上的键集分页的通用 SQL 谓词

Generic SQL predicate to use for keyset pagination on multiple fields

高性能分页的一个常见解决方案是使用索引字段,每个新 "page" 从前一页的最后一个值开始。例如,对于这样的数据集(假设类别和 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,如果我们想要 return 所有 Red 类别记录(假设按类别、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 是全局唯一的,它也适用于多个字段,但事实并非如此)。

但是如果我想return所有的Red和Blue记录(假设table还包含其他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 和其他一些数据库中,有一个 "row values" 谓词语法支持这个(假设按类别、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)

之所以有效,是因为出于测试目的,类别和 ID 都被视为单个复合值。但我没有使用 PostgreSQL 或支持 "row values" 的数据库。所以问题是是否有替代解决方案适用于此(是否有 2 个或 n 个字段)?为了让它在多个变量字段上进行分页,我需要设置一个始终在多字段排序顺序中找到 "next record" 的谓词。

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

您可以随时对谓词进行表述:

(x, y) > (a, b)

如:

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

请注意第一个前缀 x >= a 促进(它不确保)在该列上使用索引。也就是说,它变成了一个"access predicate"。第二个 x = a and y > b or x > a 过滤掉多余的行,有效地成为 "filtering predicate".

这种 "tuple inequality" 谓词的措辞方式促进了索引的使用。但是,如果您比较 3、4 或更多列,它们会变得越来越复杂。

扩展 ,使用复合键进行键集分页的通用语法如下:

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