没有NESTED SELECT重写MySQL查询?

时间:2012-11-17 04:18:55

标签: mysql sql

我有一个当前正在使用嵌套选择的MySQL查询,我想知道是否可以重写查询以不使用嵌套选择,如果是这样的话?

查询如下

SELECT
  b.id,
  b.name,
  b.description,
  b.order,
  b.icon,
  b.locked,
  u.username     AS lastPoster,
  p.time         AS lastPostTime,
  p1.subject     AS lastPostTopicSubject,
  p2.postscount  AS totalPosts,
  t1.topicscount AS totalTopics,
  p.subject      AS lastPostSubject,
  t.id           AS lastPostTopicId
FROM      kf_boards                                AS b
LEFT JOIN kf_topics                                AS t  ON (t.boardid = b.id)
LEFT JOIN (SELECT posterid, topicid, time, subject
           FROM kf_posts
           ORDER BY time DESC)                     AS p  ON (p.topicid = t.id)
LEFT JOIN (SELECT subject
           FROM kf_posts
           ORDER BY time ASC)                      AS p1 ON (p.topicid = t.id)
LEFT JOIN (SELECT COUNT(id) AS postscount
           FROM kf_posts)                          AS p2 ON (p.topicid = t.id)
LEFT JOIN (SELECT COUNT(id) AS topicscount
           FROM kf_topics)                         AS t1 ON (t.boardid = b.id)
LEFT JOIN kf_users                                 AS u  ON (p.posterid = u.id)
WHERE b.categoryid = :catid
GROUP BY b.name
ORDER BY b.order

数据库结构如下

enter image description here

任何帮助都会非常受欢迎!

谢谢!

编辑:尝试在查询下方,结果返回

enter image description here

结果应如下

enter image description here

3 个答案:

答案 0 :(得分:0)

尝试:

SELECT 
   b.id, b.name, b.description, b.order, b.icon, b.locked, 
   u.username AS lastPoster, p.time AS lastPostTime, 
   p.subject AS lastPostSubject, t.id AS lastPostTopicId
FROM kf_boards AS b
LEFT JOIN kf_topics AS t ON t.boardid = b.id
LEFT JOIN kf_posts AS p ON p.topicid = t.id
LEFT JOIN kf_users AS u ON p.posterid = u.id
WHERE b.categoryid = :catid
GROUP BY b.name 
ORDER BY b.order ASC, p.time DESC

更新:bellow是您的新查询。

SELECT b.id, b.name, b.description, b.order, b.icon, b.locked, 
    u.username AS lastPoster, MAX(p.time) AS lastPostTime, 
    p.subject AS lastPostTopicSubject, count(p.id) AS totalPosts, 
    count(t.id) AS totalTopics, p.subject AS lastPostSubject, 
    max(t.id) AS lastPostTopicId
FROM kf_boards AS b 
LEFT JOIN kf_topics AS t ON t.boardid = b.id
LEFT JOIN kf_posts AS p ON p.topicid = t.id
LEFT JOIN kf_users AS u ON p.posterid = u.id
WHERE b.categoryid = :catid
GROUP BY b.name, b.id, b.name, b.description, b.order, b.icon, 
    b.locked, u.username, p.subject
ORDER BY b.order

答案 1 :(得分:0)

似乎可以删除所有子查询, 但是如果最后一个帖子和第一个帖子就相应的话,查询会更清楚 使用子查询找到主题:

SELECT b.id, b.name, b.description, b.sortorder, b.icon, b.locked,
       u.username AS lastPoster,
       p1.time AS lastpostTime,
       p0.subject AS lastPostTopicSubject,
       COUNT(DISTINCT p.id) AS totalPosts,
       COUNT(DISTINCT t.id) AS totalTopics,
       p1.subject AS lastPostSubject,
       p1.topicid AS lastPostTopicId
FROM  kf_boards b
LEFT JOIN kf_topics t ON t.boardid = b.id
LEFT JOIN kf_posts p ON p.topicid = t.id
LEFT JOIN kf_posts p1
ON p1.time = (SELECT MAX(time) FROM kf_posts p
              INNER JOIN kf_topics t
              ON p.topicid = t.id
              WHERE t.boardid = b.id)
LEFT JOIN kf_users u ON u.id = p1.posterid
LEFT JOIN kf_posts p0
ON p0.time = (SELECT MIN(time) FROM kf_posts p0
              WHERE p0.topicid = p1.topicid)
WHERE b.categoryid = :catid
GROUP BY b.id
ORDER BY b.sortorder;

但是,以下查询使用自联接查找没有相关先前/嵌套帖子的帖子应该给出相同的答案:

SELECT b.id, b.name, b.description, b.sortorder, b.icon, b.locked,
       u.username AS lastPoster,
       lastpost.time AS lastpostTime,
       firstpost.subject AS lastPostTopicSubject,
       COUNT(DISTINCT p.id) AS totalPosts,
       COUNT(DISTINCT t.id) AS totalTopics,
       lastpost.subject AS lastPostSubject,
       lastpost.topicid AS lastPostTopicId
FROM  kf_boards b
LEFT JOIN kf_topics t ON t.boardid = b.id
LEFT JOIN kf_posts p ON p.topicid = t.id

LEFT JOIN kf_topics lasttopic ON lasttopic.boardid = b.id
LEFT JOIN kf_posts lastpost ON lastpost.topicid = lasttopic.id
LEFT JOIN kf_topics nexttopic ON nexttopic.boardid = b.id
LEFT JOIN kf_posts nextpost              -- order posts
ON nextpost.topicid = nexttopic.id       -- in same board
AND nextpost.time > lastpost.time        -- by time

LEFT JOIN kf_users u ON u.id = lastpost.posterid

LEFT JOIN kf_posts AS firstpost ON firstpost.topicid = lastpost.topicid
LEFT JOIN kf_posts prevpost              -- order posts
ON prevpost.topicid = firstpost.topicid  -- on same topic
AND prevpost.time < firstpost.time       -- by time

WHERE nextpost.id IS NULL                -- last post has no next
AND prevpost.id IS NULL                  -- first post on topic has no previous

AND b.categoryid = :catid
GROUP BY b.id
ORDER BY b.sortorder;

检查http://sqlfiddle.com/#!2/1c042/1/0

的结果

答案 2 :(得分:0)

这是一个可能有所帮助的解决方案,但是,我还有其他建议可以通过触发器简化所有查询。我稍后会解释。

我从最内层的查询开始,仅针对类别(您的参数)的电路板ID以及电路板有HAS POSTINGS(不是通过LEFT-JOIN)。从这一点来看,无论主题如何,我都会获得每个主板的最大帖子ID(只是它必须是每个连接的有效板)。

一旦我有了,下一个查询将根据最后一篇帖子重新加入posts表以确定主题...然后在同一主题ID上再次重新加入帖子。有了这个,我可以获得该主题的第一个帖子ID和总条目...所有按“板ID”分组。

这些显然只是拥有至少1板的板子,但这不是你想要的。你想要所有的董事会,无论一个帖子。所以,我回到开头再次使用相同的WHERE查询kf_boards类别ID =您的参数......这将为您提供该类别的所有电路板......

现在,您可以左键加入到最终/最大帖子和条目计数的预聚合查询...然后再次离开加入到帖子表但是TWICE ...一次为第一篇帖子(所以你可以获得最初的主题标题,时间和你可能关心的任何其他内容),以及为最后的POST获取时间,主题等...你已经拥有了这个主题的总帖子条目聚合查询。最后,在用户表的最后一个帖子上进行左连接,以查看最后发布的帖子。

我已经测试了它的语法并且它可以工作,根据实际数据无法确认。

SELECT 
      B.ID,
      B.Name,
      B.Description,
      B.Order,
      B.Icon,
      FP.Subject as FirstPostSubject,
      FP.Time as FirstPostTime,
      LP.Subject as LastPostSubject,
      LP.Time as LastPostTime,
      U.UserName as LastPostUserName,
      QryPerBoard.PostEntries
   from 
      kf_boards B
         LEFT JOIN
              ( select 
                      PQ1.ID,
                      PQ1.LastPostID,
                      MIN( P2.ID ) as FirstPostID,
                      COUNT(*) as PostEntries
                   from 
                      ( SELECT
                              B1.ID,
                              MAX( P1.ID ) as LastPostID
                           from
                              kf_boards B1
                                 join kf_topics T1
                                    ON B1.ID = T1.BoardID
                                    join kf_posts P1
                                       ON T1.ID = P1.TopicID
                           where
                              B1.CategoryID = 1    <-- Insert your Category Parameter ID here
                           group by
                              B1.ID ) as PQ1
                      LEFT JOIN kf_posts P1
                         ON PQ1.LastPostID = P1.ID
                         LEFT JOIN kf_posts P2
                            ON P1.TopicID = P2.TopicID
                   group by
                      PQ1.ID ) QryPerBoard
            ON B.ID = QryPerBoard.ID
            LEFT JOIN kf_posts FP
               ON QryPerBoard.FirstPostID = FP.ID
            LEFT JOIN kf_posts LP
               ON QryPerBoard.LastPostID = LP.ID
               LEFT JOIN kf_users U
                  ON LP.PosterID = U.ID
   where
      B.CategoryID = 1    <-- Insert your Category Parameter ID here (second copy for parameter)

现在,我将如何调整以防止递归级别的查询,尤其是对于网站。使用触发器。创建并保存POST时,请使用触发器执行一些操作...

修改了关于触发影响的思考。

使用最新的主题ID更新kf_Boards,为任何相应的主板ID创建任何帖子,这样您以后就不必再寻找它,只需采取最后一个并随之运行。此外,更新TOPIC记录。为主题提供第一个帖子,最后一个帖子和总帖子的列。如果是该主题的第一篇帖子,请使用新ID更新第一篇和最后一篇文章,并增加总帖子数。

合并这些触发器以更新“额外”列的时间将为将来的查询节省这些复杂性。你基本上可以做类似

的事情
select
      B.*,
      LP.Fields,  <obviously apply specific fields you want per table>
      FP.Fields,
      U.Fields
   from
      kf_boards B
         LEFT JOIN kf_topics T
            on B.LastTopicID = T.ID
            LEFT JOIN kf_posts FP
               on T.FirstPostID = FP.ID
            LEFT JOIN kf_posts LP
               on T.LastPostID = LP.ID
               LEFT JOIN kf_users U
                  on LP.PosterID = U.ID
   where
      B.CategoryID = 1  <-- your parameterID