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

时间:2019-06-22 22:25:30

标签: sql pagination keyset

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

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

如果要返回所有Red类别记录(假设ORDER BY类别,ID),则假设页面大小(较小)为1,

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)

之所以可行,是因为通过分页,“键集”仅使用ID字段(如果ID全局唯一,则它也可以在多个字段上使用,而不是)。

但是,如果我想返回所有红色和蓝色记录(假设该表还包含其他类别),则一次仍要一页(假设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)

之所以起作用,是因为出于测试目的,类别和ID都被视为单个复合值。但是我没有使用PostgreSQL或支持“行值”的数据库。因此,问题是是否有替代解决方案对此适用(无论是2个字段还是n个字段)?为了使其能够在多个变量字段上进行分页,我需要使用一个谓词,该谓词将始终以多字段排序顺序查找“下一条记录”。

PS:当然可以使用OFFSET / LIMIT或SKIP / LIMIT分页,但是对大数据集都不有效,这就是为什么我要使用“ keyset”分页。

2 个答案:

答案 0 :(得分:1)

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)好,但是您可以使用自己选择的语言对此进行编程。您只需要遍历一组字段,每个连续的字段都会扩展为包含先前字段中的{field} = {value} AND

答案 1 :(得分:0)

您始终可以对谓词进行措辞:

(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或更多列,它们将变得越来越复杂。