【问题标题】:In MySQL, how to retrieve records after/before a record with a given ID?在 MySQL 中,如何在给定 ID 的记录之后/之前检索记录?
【发布时间】:2018-05-06 11:34:58
【问题描述】:

作为实现 GraphQL API 的一部分,我尝试在分页之前/之后提供。例如,返回 具有给定 ID 的记录之后的 LIMIT 记录。这必须适用于已经存在任意 JOIN、WHERE 和 ORDER BY 子句的任意 SELECT 语句。

与仅使用页码相比,这种分页的好处是,当基础数据发生变化时,它可以更接近地返回预期的结果页面。

如果我可以在 after 使用它,我也可以通过反转 ORDER BY 子句使其在 before 使用,所以这里我将只关注 before em>在条件之后。

修改给定 SELECT 语句以完成此操作的最简单或最有效的方法是什么?

我的第一个想法是在 WHERE 子句中添加一个 AND 条件,将结果限制为那些来自 ORDER BY 子句的列值大于或等于它们在具有给定 ID 的记录中的值的结果。但这似乎不起作用,因为 ORDER BY 子句列中没有唯一性的期望,因此无法知道目标记录将落在结果中的哪个位置,因此无法知道如何设置 LIMIT返回正确数量的记录。

另一种方法是首先在初始 SELECT 语句中发现目标记录的偏移量,然后将 LIMIT offset+1, limit 子句添加到具有发现的偏移量的初始 SELECT 语句。

MySQL 没有row_count() 或类似功能,但可以添加行号like this

SELECT @rownum:=@rownum+1 ‘rank’, t.* 
FROM my_table t, (SELECT @rownum:=0) r ORDER BY field2;

那么上面可以作为子查询来获取目标记录的排名,例如

SELECT rank FROM (SELECT @rownum...) WHERE id = 42

然后使用该排名作为最终查询的偏移量:

SELECT ... LIMIT (rank + 1), 100

这可以作为具有多个子查询的单个查询来完成,例如

SELECT ... LIMIT (SELECT rank from (SELECT @rownum...) ...) + 1, 100

但是这三个查询方法似乎是一种复杂且不是非常快速的方法来执行一个非常频繁使用的操作,这给我们的数据库服务器带来了比我们希望的更高的负载。

有没有更好的方法来做到这一点?

编辑:要求提供特定示例。假设我想从包含 10 篇文章的表格中获取包含 2 篇文章的页面。我们将对这个查询进行分页:

select id, title from articles order by title desc

桌子:

id, title
1, "a"
3, "b"
4, "c"
6, "d"
7, "e"
8, "f"
9, "g"
10, "h"
11, "i"
12, "k"

因此,当请求页面之后 id 6 时,正确的记录将是4, "c"3, "b"。这需要适用于任意 WHERE 和 ORDER BY 子句。

【问题讨论】:

  • 获取最后显示的item的ID,然后取后面的25行?
  • @Andomar,问题是,对于任意排序的 SELECT 语句,我如何“获取之后的 25 行”?
  • 解决的关键是你所说的"after"。您是指具有较晚日期、较高主键等的行吗?通过创建一个选择该行作为“锚点”(IOW,所需行之后的行)并加入该子查询的子查询,您可以使用 order by 来获取之后的行。
  • 好吧,如果我理解正确的话,我会使用“rank”进行查询,并且在 where 部分中存在相同的“rank 查询”。我不知道我是否足够清楚。您能否添加一些示例数据以及之后和之前的结果?
  • 如果是简单的分页问题,​​那么只需使用 LIMIT startval,numrow 语法重复上一个查询

标签: mysql sql pagination


【解决方案1】:

在没有任何实际示例表的情况下,以下查询是可以使用的各种蓝图。

它从表中选择列值大于子查询中的行的行。 子查询选择表中的目标行,它是所需行的“起点”。

在下面的示例中,col1 是排名列。将子查询中的 WHERE 子句更改为选择起始行所需的任何内容。

对于分页,更改 LIMIT 子句以表示之前的分页查询。

SELECT
    col1,
    col2,
    etc
FROM table a
JOIN (
    SELECT col1
    FROM table c
    WHERE conditions
    LIMIT 20,1
    ) b
    ON a.col1 > b.col1
ORDER BY col1
LIMIT 20;

【讨论】:

  • 这是通常和预期的分页方式,但这不是我所追求的。我希望不是基于页码或上一页(可能没有),而是基于特定行来拉页面。
  • 然后将 LIMIT 放到子查询中,然后在 condition 中放入你需要的任何东西来选择 "particular" 行。
【解决方案2】:

您可以根据您订购的任何内容简单地选择目标行之前或之后的行。假设您在 id 上有一个索引,这应该只需要一个相对便宜的子查询:

SELECT id, title
FROM articles
WHERE title < (SELECT title FROM articles WHERE id = '6')
ORDER BY title DESC
LIMIT 0,2;

下一组是

LIMIT 2,2

上一组需要&gt;= 运算符:

SELECT id, title
FROM articles
WHERE title >= (SELECT title FROM articles WHERE id = '6')
ORDER BY title DESC
LIMIT 0,2;

等等……

使用更具体的数据和表结构给出更简洁的答案会更容易,但希望这会有所帮助。

【讨论】:

  • 这是我在问题中概述的第一种方法,由于您描述的非不同行的问题,我不得不拒绝。所以是的,我回到通过行计数来获取偏移量。感谢您对此进行调查。
  • 解决这个问题的一种方法是在获取前面的行时,您可以包含带有问题 id 的行,理论上,dbms 应该为任何一个返回相同的顺序,所以无论如何您应该仍然得到你想要的结果。如果你能提供一个你认为这行不通的实际数据集,我很乐意花更多时间在上面。
  • @Mori 你明白我关于包含在一侧或另一侧的建议吗?我刚刚对此进行了测试,它适用于重复的 title 值。查看更新的答案...
  • 我已经更改了示例数据集。我认为对于这些数据,您的方法将根据需要返回 ID 为 1 和 3 而不是 3 和 4 的记录。
  • 如果您不想包含 id 6,请删除运算符中的 = 符号。您将在一侧或另一侧需要它,否则您的 id 将始终被排除在每个查询之外。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-21
  • 1970-01-01
相关资源
最近更新 更多