【问题标题】:MySQL grouping isn't respecting ORDER BYMySQL 分组不尊重 ORDER BY
【发布时间】:2016-02-14 10:11:39
【问题描述】:

我正在尝试执行 MySQL 查询以查找网络论坛中最近活跃的线程(以及每个线程的最新评论)。线程存储在两个表中,forum_topicsforum_responses,其中每个forum_topic 有许多forum_responses

在这里,我在forum_reponsesforum_topics 上进行搜索,对forum_response.id 进行降序排序:

select t.id, t.title, r.id, r.body 
    from forum_responses r 
    inner join forum_topics t on (r.forum_topic_id = t.id) 
    order by r.id desc;

+----+--------------+----+----------------------------------+
| id | title        | id | body                             |
+----+--------------+----+----------------------------------+
| 17 | New Topic    | 69 | yes                              |
| 19 | Test Topic 1 | 68 | This is a test                   |
| 17 | New Topic    | 64 | hey yo                           |
| 19 | Test Topic 1 | 63 | Test Topic Starter               |
| 18 | Test Topic   | 62 | Test.                            |
| 18 | Test Topic   | 61 | Test                             |
| 17 | New Topic    | 60 | Another test response.           |
| 17 | New Topic    | 59 | Test response.                   |
| 17 | New Topic    | 54 | What should this topic be about? |
+----+--------------+----+----------------------------------+

好的,到目前为止一切顺利。但它正在返回重复项 - 我只想拥有最近回复的论坛主题。所以我在查询中添加了一个 GROUP BY,以便我们可以按主题 ID 分组:

select t.id, t.title, r.id, r.body 
    from forum_responses r 
    inner join forum_topics t on (r.forum_topic_id = t.id) 
    group by t.id 
    order by r.id desc;

+----+--------------+----+----------------------------------+
| id | title        | id | body                             |
+----+--------------+----+----------------------------------+
| 19 | Test Topic 1 | 63 | Test Topic Starter               |
| 18 | Test Topic   | 61 | Test                             |
| 17 | New Topic    | 54 | What should this topic be about? |
+----+--------------+----+----------------------------------+

但是现在,我们遇到了一个问题:它是按论坛主题 ID 进行分组的,但与直觉相反,我们没有按照最近的活动对论坛主题进行排序,并且相关的论坛回复也不是最新的。

这里出了什么问题?有没有办法更改此查询,以便我获得最近参与论坛主题的列表,以及它们各自最近的 cmets?

【问题讨论】:

  • 如果我正确理解了您的目标...您需要在子查询中分组以获取每个主题的最新响应 ID,然后将其加入响应以获取正文回应。
  • 您按主题 ID 分组。因此,每个主题都会得到一个结果行。现在让我们看看你展示了什么:它的 ID、它的标题、它的响应 ID 之一、它的响应主体之一。您可以按照每个主题任意选择的响应 ID 对结果进行排序。为什么它只是主题响应 ID 和正文之一?因为你没有告诉 DBMS 你想要哪一个。 (但不可否认,在 MySQL 中要告诉它并不容易)。
  • 您说要显示最新的主题,但您似乎要显示所有个主题,每个主题都有其最新的响应。或不?你到底想要什么?
  • @Gordon Linoff:我想你在这里误解了 Uueerdo。如果 schnauss 实际上想要所有主题及其最新响应,您可以通过查找每个主题的最新响应 id(在派生表查询中)来解决此问题,然后使用这些来访问响应记录。
  • @GordonLinoff 我没有在子查询中提及 ordering ;我是说 grouping 必须合二为一。 Thorston 更好地理解和阐述。

标签: mysql sql sorting group-by


【解决方案1】:

这就是我在最初评论中的建议;你需要像这样单独分组:

SELECT t.id, t.title, r2.id, r2.body 
FROM (
   SELECT forum_topic_id, MAX(id) AS lastResponseID 
   FROM forum_responses 
   GROUP BY forum_topic_id
) AS r
INNER JOIN forum_responses AS r2 
   ON r.forum_topic_id = r2.forum_topic_id AND r.lastResponseID = r2.id
INNER JOIN forum_topics AS t 
   ON r.forum_topic_id = t.id
ORDER BY t.id;

在子查询中包含 forum_topics(它是 title 字段)会更快(更好地利用索引),但这在很大程度上取决于数据分布;加入数以百万计的索引行(对其主题的每个响应)可能比相对较少的未索引行(对其主题的最新响应)慢。

【讨论】:

  • 我明白你的意思(AND on JOIN)。
  • 这绝对不是我对您原始评论的理解。这是正确的。
【解决方案2】:

诊断

您的第二个查询的问题是您正在执行GROUP BY t.id,但选择的其他列(t.titler.idr.body)不是聚合函数。这实际上是标准 SQL 下的错误,但 MySQL 不会抱怨,除非您启用服务器范围的 ONLY_FULL_GROUP_BY 设置。相反,MySQL 只会给您提供不确定的结果。

建议的解决方案

select t.id, t.title, r.id, r.body
    from (
            select forum_topic_id, max(id) as id
                from forum_responses
                group by forum_topic_id
        ) as latest
        inner join forum_topics t on latest.forum_topic_id = t.id
        inner join forum_responses r on latest.id = r.id
    order by r.id desc;

超越 MySQL

使用功能更强大的数据库,可以使用窗口函数(也称为分析函数)来编写它。然后,您可以避免其中一个联接。在 PostgreSQL、MS SQL Server 或 Oracle 上,查询看起来更像这样:

select t.id, t.title, r.id, r.body
    from (
        select forum_topic_id, id, body
            from forum_responses
            where 1 = rank(id over partition by forum_topic_id order by id desc)
        ) as r
        inner join forum_topics t on t.id = r.forum_topic_id
    order by r.id desc;

【讨论】:

    猜你喜欢
    • 2020-06-30
    • 1970-01-01
    • 2014-03-03
    • 1970-01-01
    • 1970-01-01
    • 2013-09-20
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    相关资源
    最近更新 更多