MySQL重构选择子查询到连接

时间:2017-01-23 11:09:57

标签: mysql sql

我这里有4个单独的选择子查询,只选择我需要的1列 - 但我知道这是低效的。

如何通过连接实现同样的目标?我已尝试使用group by连接子集中的子查询,但遗憾的是无法使其工作。

SELECT [columns needed],
(SELECT posts.created_at FROM posts WHERE posts.thread_id = threads.id ORDER BY posts.created_at DESC LIMIT 1) as lastpost_created_at,
(SELECT users.username FROM posts INNER JOIN users on posts.user_id = users.id WHERE posts.thread_id = threads.id ORDER BY posts.created_at DESC LIMIT 1) as lastpost_username,
(SELECT users.avatar FROM posts INNER JOIN users on posts.user_id = users.id WHERE posts.thread_id = threads.id ORDER BY posts.created_at DESC LIMIT 1) as lastpost_avatar,
(SELECT COUNT(*) FROM posts WHERE posts.thread_id = threads.id) as replies
FROM threads
INNER JOIN posts ON threads.post_id = posts.id
INNER JOIN users ON posts.user_id = users.id
ORDER BY lastpost_created_at DESC

1 个答案:

答案 0 :(得分:1)

我不确定我是否正确解释,但似乎你想得到:

  • 每个帖子,
  • 每个帖子的最新帖子
  • 包括发布帖子的用户详细信息(头像等)
  • 每个帖子的帖子总数

我已将SQLfiddle放在一起,我认为这些提供所有这些,请在下面查询:

select threads.id,
       lastpost_created_at.lastpost,
       posts.user_id,
       /* posts.other_fields, */
       users.avatar,
       /* users.other_fields, */
       post_counts.replies
from   threads
       left join 
       (
          /* first we aggregate the posts by thread_id 
             to get the most recent created timestamp. */
          /* Note that this assumes unique timestamp per post / thread_id */
          select  max( posts.created_at ) as lastpost, 
                  thread_id
          from    posts
          group by thread_id
       ) as lastpost_created_at
       on threads.id = lastpost_created_at.thread_id

       /* Now we join on to the posts table to get the associated user id */
       /* (and any other fields you want from posts)*/
       left join 
       posts
       on threads.id = posts.thread_id
       and posts.created_at = lastpost_created_at.lastpost

       /* Then grab the user avatar etc */
       left join
       users
       on posts.user_id = users.id

       /* and finally get the total number of replies per thread */
       left join
       (
          select  thread_id,
                  count(*) as replies
          from    posts
          group by thread_id
       ) as post_counts
       on threads.id = post_counts.thread_id

一个注意事项:如果单个帖子的两个帖子具有完全相同的created_at时间戳,则会导致两个帖子都出现重复的行。

修改

我把一个只在一个重复时间戳的情况下返回一个结果的查询放在一起:

select threads.id,
       most_recent_post.created_at,
       most_recent_post.user_id,
       users.avatar,
       post_counts.replies
from   threads
       left join 
       (
          /* Get the most recent post per thread, ordered by created at and ID */
          select  di.id, di.user_id, di.created_at, di.thread_id
          from    (
                      select  thread_id, max(created_at) AS created_at
                      from    posts d
                      group by thread_id
                  ) as dd
                  inner join    
                  posts as di
                  on di.id = (
                      select  id
                      from    posts ds
                      where   ds.thread_id = dd.thread_id
                              and ds.created_at = dd.created_at
                      order by id desc
                      limit 1
                  )
       ) as most_recent_post
       on threads.id = most_recent_post.thread_id

       /* Then grab the user avatar etc */
       left join
       users
       on most_recent_post.user_id = users.id

       /* and finally get the total number of replies per thread */
       left join
       (
          select  thread_id,
                  count(*) as replies
          from    posts
          group by thread_id
       ) as post_counts
       on threads.id = post_counts.thread_id

更新了SQLfiddle here。这使用了Groupwise Maximum found here的技术。