从特定记录/项目键分页开始获取范围

Get range starting from a specific record / item-keyed pagination

我的最终目标是创建分页,我想知道是否有更好或正确的方法来做事。

给定一个包含多个排序列的 Table,如 ORDER BY s1, s2, s3 我想在给定记录之后获取下一个 n 记录。假设我知道完整的记录,包括它的 s1, s2, s3.

的值

到目前为止我的想法是这样的:

-- Given current entry (prefixed with e_), get next n records

SELECT * FROM Entries
  WHERE
    (s1 < e_s1) OR
    (s1 = e_s1 AND s2 < e_s2) OR
    (s1 = e_s1 AND s2 = e_s2 AND s3 < e_s3)
  ORDER BY s1, s2, s3
  LIMIT n;

虽然我确实在 (s1, s2, s3) 上创建了索引,但我觉得这不是最有效或最优雅的方法。查询也会变得臃肿,排序键越多。即便如此,这个查询很容易适应向后看,我也需要能够做到。

我无法访问记录的当前偏移量,我担心这样做可能效率较低,因为我必须使用子查询和聚合来计算它 (window函数在我的平台上不可用,因为它是 android).

是否有better/moreelegant/efficient查询数据的方法?

Here's an SQL Fiddle with some sample data


对任何使用此方法的人来说很重要:您应该始终有一个唯一列作为排序和比较中的最后一列加上相应的索引。这使索引性能更好,并在您有两个相等的行时提供必要的平局。

当您的列组合存在唯一约束时,这当然是不必要的[即(s1, s2, s3)]

上有一个唯一索引

SQLite 支持元组比较,所以你可以这样做:

SELECT * 
FROM Entries
WHERE (s1, s2, s3) < (29, 30, 30)
ORDER BY s1, s2, s3
LIMIT 5;

更好的方法是将 OFFESTLIMIT 一起使用,然后无需在 WHERE 中添加任何条件。您检索的每个页面,您只需要记住偏移量(或作为“下一个标记”返回给用户)。偏移量当然是从 0 开始的。每次调用页面时,您都会将偏移量增加(如果您想向后减少,则减少)。

像这样:

SELECT * FROM Entries
  ORDER BY s1, s2, s3
  LIMIT n OFFSET offset

Note1: the syntax of the limit could also be written: LIMIT {offset},{limit}.

Note2: the ORDER BY must be unique, to make sure we always get same entry at same offset.

https://www.sqlite.org/lang_select.html#limitoffset

如果你不想使用偏移量,你可以用填充所有索引并进行一次比较。这看起来更优雅:

SELECT * FROM Entries
  WHERE 
     PRINTF('%04d%04d%04d',s1,s2,s3) < PRINTF('%04d%04d%04d',29,30,30)
  ORDER BY s1, s2, s3
  LIMIT 5;

简单填充取自here