3

我正在尝试获取

  • 最新线程(id、主题、时间戳、author_id)和
  • 最新帖子(id、thread_id、时间戳、author_id)
  • 每个论坛的(ID,名称)
  • 按最新帖子排序,与线程的创建日期无关。

为什么?

我希望能够显示以下详细信息:

"The latest Answer of forum $forum_id was given on Question $thread_id. Here it is: $post_id"

SELECT  f.id AS forum_id,
        f.name AS forum_name,
        t.id AS thread_id,
        t.topic AS thread_topic,
        t.ts AS thread_timestamp,
        p.id AS post_id,
        p.content AS post_content,
        p.ts AS post_timestamp

 FROM   forums f,
        threads t,
        posts p

WHERE   f.id = t.forum_id 
  AND   t.id = p.thread_id

GROUP BY f.id
ORDER BY p.ts

任何建议,如何更改 SQL 以获得尽可能高的性能?我试图避免子查询,但我思想开放!

提前致谢!

4

2 回答 2

3

由于 MySQL 不支持窗口函数,我认为没有子查询就没有办法做到这一点:

SELECT  f.id AS forum_id,
    f.name AS forum_name,
    t.id AS thread_id,
    t.topic AS thread_topic,
    t.ts AS thread_timestamp,
    p.id AS post_id,
    p.content AS post_content,
    p.ts AS post_timestamp

FROM   forums f
JOIN (SELECT t2.forum_id, max(p2.ts) as ts
      FROM posts p2
      JOIN threads t2 ON p2.thread_id = t2.id
      GROUP BY t2.forum_id) max_p ON f.id = max_p.forum_id
JOIN   posts p ON max_p.ts = p.ts
JOIN   threads t ON f.id = t.forum_id AND p.thread_id = t.id
ORDER BY p.ts

自然地,缓存最新结果可以让您在没有调用 MAX() 的性能损失的情况下执行此操作,但是使用正确的索引,这应该不是什么大问题......

更新

包含没有帖子的线程和没有线程的论坛的最简洁的方法是使用 LEFT JOINs 而不是 INNER JOINs:

SELECT  f.id AS forum_id,
    f.name AS forum_name,
    t.id AS thread_id,
    t.topic AS thread_topic,
    t.ts AS thread_timestamp,
    p.id AS post_id,
    p.content AS post_content,
    p.ts AS post_timestamp

FROM   forums f
LEFT JOIN (SELECT t2.forum_id, max(COALESCE(p2.ts, t2.ts)) as ts, COUNT(p2.ts) as post_count
      FROM threads t2 
      LEFT JOIN posts p2 ON p2.thread_id = t2.id
      GROUP BY t2.forum_id) max_p ON f.id = max_p.forum_id
LEFT JOIN   posts p ON max_p.ts = p.ts
LEFT JOIN   threads t ON f.id = t.forum_id AND (max_p.post_count = 0 OR p.thread_id = t.id)
ORDER BY p.ts
于 2013-06-20T20:06:18.787 回答
1

我可以想到两种“正确”的方式来做到这一点。第一个是使用连接和子查询:

SELECT  f.id AS forum_id,
        f.name AS forum_name,
        t.id AS thread_id,
        t.topic AS thread_topic,
        t.ts AS thread_timestamp,
        p.id AS post_id,
        p.content AS post_content,
        p.ts AS post_timestamp
 FROM   forums f join
        threads t
        on f.id = t.forum_id join
        posts p
        on t.id = p.thread_id
WHERE   t.ts = (select ts from threads t2 where t2.forum_id = t.forum_id order by ts desc limit 1) and
        p.ts = (select ts from posts p2 where p2.thread_id = p.thread_id order by ts desc limit 1)
GROUP BY f.id
ORDER BY max(p.ts)

这种方法的问题在于,这会返回最新的线程和该线程上的最新帖子。解决这个问题很麻烦(这可能是你真正想要的。)

子查询获取 和 的最新threads日期posts。性能取决于您拥有的索引。这可能是可以接受的。这是标准 SQL。

另一个是substring_index()/的技巧group_concat(),这是 MySQL 特有的:

SELECT  f.id AS forum_id,
        f.name AS forum_name,
        substring_index(group_concat(t.id order by t.ts desc separator '|'), '|', 1) AS thread_id,
        substring_index(group_concat(t.topic order by t.ts desc separator '|'), '|', 1)  AS thread_topic,
        substring_index(group_concat(t.ts order by p.ts desc separator '|'), '|', 1)  AS thread_timestamp,
        substring_index(group_concat(p.id order by p.ts desc separator '|'), '|', 1)  AS post_id,
        substring_index(group_concat(p.content order by p.ts desc separator '|'), '|', 1)  AS post_content,
        substring_index(group_concat(p.ts order by p.ts desc separator '|'), '|', 1)  AS post_timestamp
 FROM   forums f join
        threads t
        on f.id = t.forum_id join
        posts p
        on t.id = p.thread_id
GROUP BY f.id
ORDER BY max(p.ts);

此版本的性能可能会更好(因为您已经承担了 a 的开销group by)。必须选择分隔符,因此它不在任何值中。否则,只会出现分隔符之前的部分。

一个优点是线程和帖子是独立处理的,因此您可以获得最新的线程,并且分别获得最新的帖子。您可以通过更改order by.group_concat()

此外,要获得您想要的订购,您需要订购max(p.ts)而不是仅订购p.ts. 后者将通过论坛上的任意时间戳进行排序;不能保证它会是最新的。

于 2013-06-20T20:08:07.780 回答