【问题标题】:SQL - find next and previous rows given a particular WHERE clauseSQL - 在给定特定 WHERE 子句的情况下查找下一行和上一行
【发布时间】:2009-03-14 00:50:46
【问题描述】:

我有一个 bbPress 论坛使用的名为 bb_posts 的 MySQL 表。它有一个名为 topid_id 的自动增量字段和另一个名为 topic_poster 的字段。

我正在尝试编写一个函数来查找“同一作者的下一篇文章”。因此,例如,假设用户在显示主题 123 的特定页面上。如果您执行 SQL 查询:

SELECT *
FROM `bb_topics`
WHERE `topic_poster` = 5
ORDER BY `topic_id` ASC

这可能会返回以下行:

topic_id 主题海报 6 5 50 5 123 5 199 5 2039 5

我想做的是编写一个返回这两行的 SQL 查询:

topic_id 主题海报 50 5 199 5

这将是 topic_id 为 123 的行之前的行,以及该行之后的行。

如果在一个查询中做这件事太难了,把它分成两个查询肯定没问题...

我想避免执行整个 SQL 查询(“SELECT * FROM bb_topics WHERE topic_poster = 5”)并循环遍历结果,因为结果集有时很大。

这可能吗? :-)

【问题讨论】:

  • FYI SQL 分析函数让您可以使用 LEAD 和 LAG 函数执行此操作。不确定 mySQL 是否支持它们,但 SQL2008 支持。

标签: sql mysql


【解决方案1】:

下一个:

SELECT * FROM `bb_topics` 
      WHERE `topic_id` = 
      (select min(`topic_id`) FROM `bb_topics` where `topic_id` > 123
         and `topic_poster` = 5)

上一个:

SELECT * FROM `bb_topics` 
      WHERE `topic_id` = 
      (select max(`topic_id`) FROM `bb_topics` where `topic_id` < 123
         and `topic_poster` = 5)

两者:

SELECT * FROM `bb_topics` 
      WHERE `topic_id` = 
      (select min(`topic_id`) FROM `bb_topics` where `topic_id` > 123
                     and `topic_poster` = 5)
      or `topic_id` = 
      (select max(`topic_id`) FROM `bb_topics` where `topic_id` < 123
                        and `topic_poster` = 5)

【讨论】:

  • 这很有希望!抱歉这个新问题,但是我知道的“123”(topic_id)应该放在哪里?另外,它是 SELECT * FROM bb_topics AS t 吗?
  • 是的,AS 可能是对的。我更熟悉 SQL Server 语法。让我再为你编辑一次。
  • 如果您已经拥有当前主题 ID 和发帖人 ID,则可以这样做。正如 le dorfier 所展示的,您也可以在使用自联接的单个查询中执行相同的操作(它会在单个查询中为您的结果集中的每一行提供下一个和上一个 id - 更优雅)。
  • 除了他的查询不起作用 - 它会给你这张海报的所有更高和更低的主题 ID。您仍然需要在连接条件中使用具有 min() 和 max() 函数的子查询。
  • cdonner,这太棒了,谢谢!我确信这在 SQL Server 中有效,但对于 MySQL,我认为语法需要是:SELECT * FROM bb_topics WHERE topic_id = (SELECT MIN(topic_id) FROM bb_topics WHERE topic_id > 123 和topic_poster = 5) 这行得通! :-)
【解决方案2】:

看看这个older question

我的猜测是 LIMIT 1 的 UNION 比我在这里回答中的聚合表现更好。

【讨论】:

    【解决方案3】:

    这是一种微妙但有效的方法来实现这一点,它甚至适用于不支持子查询的旧版本 MySQL --

    SELECT *    
    FROM bb_topics AS bb1    
    LEFT JOIN bb_topics AS bb2    
        ON bb1.topic_poster = bb2.topic_poster    
        AND bb2.topic_id > bb1.topic_id    
    LEFT JOIN bb_topics AS bb3  
        ON bb1.topic_poster = bb3.topic_poster    
        AND bb3.topic_id > bb1.topic_id    
        AND bb3.topic_id < bb2.topic_id  
    WHERE bb1.topic_poster = 5  
        AND bb3.topic_id IS NULL  
    

    这将为您提供下一个相邻的帖子。如果不是不言自明,我可以为下一篇文章填写对称条款。

    编辑 - 次要更正的 alii。

    【讨论】:

    • 这不太正确,因为连接会给你带来比你想要的更多的东西。没有办法绕过子查询。
    • 然后我没有正确理解表达式。最多只能有 1 行具有更高的 topic_id,并且在它和目标 id 之间没有。 (“topic_id IS NULL”表示不匹配)。
    【解决方案4】:

    注意:在投反对票之前,请阅读评论和问题的第一次修订;-)

    根据定义RDBMS 表上的数据没有顺序。这意味着当您运行查询时,如果您不指定 ORDER BY 子句,则一个查询的顺序可能与另一个查询不同。此外,您必须考虑下一个查询将(或至少可能)返回不同的顺序

    假设可能是数据库上的另一个用户插入了一条新记录,并且 RDBMS 考虑将它放在您正在考虑的行之间。或者 DBA 正在在线将表移动到另一个表(例如,这在 Oracle 中是可能的),在这种情况下,可以随时更改数据。

    对于您的问题,您必须构建一种查询结果缓存来确定查询的前后行。

    【讨论】:

    • 我添加了一个 ORDER BY 子句来澄清。谢谢!
    • 我对你投了反对票,因为这是一个简单的查询问题,与记录的排序顺序无关。
    • @cdonner,问题的第一个修订版显示了没有 ORDER BY 的查询。
    • @FerranB,我知道,但这无关紧要。他想获得给定行的下/上一行,而不管行的排序顺序。当然,您可以在内存中的结果集中执行此操作,那么您是对的-这很重要。但他要求提供 SQL。
    • @joe_mucchiello,你正在分裂头发。有一个隐含的顺序(增加主题 ID)。我认为他在问题中充分解释了这一点。
    猜你喜欢
    • 2011-06-19
    • 1970-01-01
    • 1970-01-01
    • 2017-05-31
    • 2020-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多