【问题标题】:Determine on what page a record is确定记录在哪个页面上
【发布时间】:2014-02-16 05:43:46
【问题描述】:

如何确定某条记录在哪个页面上?

假设我使用这样的查询在每页显示 5 条记录:

SELECT * FROM posts ORDER BY date DESC LIMIT 0,5
SELECT * FROM posts ORDER BY date DESC LIMIT 5,5
SELECT * FROM posts ORDER BY date DESC LIMIT 10,5

样本数据:

id  | name | date
-----------------------------------------------------
1   | a    | 2013-11-07 08:19               page 1
2   | b    | 2013-12-02 12:32
3   | c    | 2013-12-14 14:11
4   | d    | 2013-12-21 09:26
5   | e    | 2013-12-22 18:52      _________ 
6   | f    | 2014-01-04 11:20               page 2
7   | g    | 2014-01-07 21:09
8   | h    | 2014-01-08 13:39
9   | i    | 2014-01-08 16:41
10  | j    | 2014-01-09 07:45      _________
11  | k    | 2014-01-14 22:05               page 3
12  | l    | 2014-01-21 17:21

有人可以编辑一条记录,比如id = 7,或者插入一条新记录(id = 13)。如何确定该记录在哪个页面上?原因是我想显示包含刚刚编辑或添加的记录的页面。

好吧,如果记录被编辑,我想我可以只显示相同的页面。但问题是何时添加记录。列表可以按名称排序,新记录可以放在任何地方:(

有什么方法可以像SELECT offset WHERE id = 13 ORDER BY date LIMIT 5 这样返回 10 的查询吗?

【问题讨论】:

  • date 记录了什么?这是一个列的可怕名称 - 它仅描述类型,而不是重要的 - 包含的信息。例如,如果它真的是updatedAt,我会假设页面会page 1....(除非更新频率太快,在这种情况下无论如何你都会遇到分页问题)。否则,您必须对所有页面进行排序(基于任何内容),分配排名(在页面中放置),然后找到给定条目的排名(以及页面)。如果您的条目集很大,这可能会很昂贵...... OLAP 函数/CTE 有帮助,但是......
  • 它是createdAt,而不是updatedAt。如果我必须得到整套,我不明白排名有什么帮助:s
  • 我说的是 OLAP 函数,例如 ROW_NUMBER(),这就是所谓的“窗口函数”(不过,SQLite 似乎没有这个特性)。该函数根据指定的顺序分配一个递增的数字,然后可以使用它返回偏移量和东西。虽然,考虑到这一点,这里还有另一种可能的解决方案......写答案......

标签: sql sqlite select pagination offset


【解决方案1】:

为了这个例子,我们假设刚刚添加了条目7(并且可能有重复的名称) - 您需要做的第一件事是找出在该条目之前有多少条目(基于name),因此:

SELECT COUNT(*) 
FROM Posts
WHERE name <= 'g'
      AND id < 7

这里,id 被用作“决胜局”列,以确保排序稳定。它还假设我们也知道 id 的值 - 鉴于非关键数据可以重复,您需要这种功能。
无论如何,这为我们提供了这一行之前的行数 (6)。通过一些整数除法运算(基于LIMIT),我们现在可以得到相关信息:

(int) ((6 - 1) / 5) = 1

...这是一个索引为 0 的页面,但是(即条目 1 - 5 出现在页面“0”上);但是,在这种情况下,它对我们有利。请注意,我们必须从初始计数中减去 1,因为第一个是 1,不是 0 - 否则,条目 5 将出现在第二页,而不是第一页。
我们现在有了 page 索引,但是我们需要把它变成 entry 索引。一些简单的乘法可以为我们做到这一点:

(1 * 5) + 1 = 6

(忽略这与计数相同 - 在这种情况下这是巧合)。
这为我们提供了页面上第一个条目的索引,即 OFFSET 的值。
我们现在可以编写查询:

SELECT id, name, date
FROM Posts
ORDER BY name, id
LIMIT 5 OFFSET 6

(请记住,如果我们假设name 可能是重复的,我们要求 id 保证数据的稳定排序!)。
这是对数据库的两次访问。令人惊讶的是,SQLite 允许 LIMIT/OFFSET 值作为 SQL 子查询的结果(请记住,并非所有 RDBMS 甚至都允许它们成为 主变量,这意味着只能通过动态更改SQL。尽管在至少一种情况下,数据库有ROW_NUMBER() 来弥补这一点......)。但是,我无法摆脱重复的子查询。

SELECT Posts.id, Posts.name, Posts.date, Pages.pageCount
FROM Posts
CROSS JOIN (SELECT ((COUNT(*) - 1) / 5) + 1 as pageCount 
      FROM Posts
      WHERE name <= 'g'
            AND id < 7) Pages
ORDER BY name
LIMIT 5, (SELECT ((COUNT(*) - 1) / 5) * 5 + 1 as entryCount 
      FROM Posts
      WHERE name <= 'g'
            AND id < 7);

(以及工作中的SQL Fiddle example)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多