0

我有一个当前使用嵌套选择的 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

并且数据库结构如下

在此处输入图像描述

任何帮助将不胜感激!

谢谢!

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

在此处输入图像描述

结果应该如下

在此处输入图像描述

4

3 回答 3

0

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

我从最内层的查询开始,该查询仅针对类别(您的参数)的板 ID,并且该板有 POSTINGS(不是通过 LEFT-JOIN)。由此,无论主题如何,我都只获得了每个版块的最大帖子 ID(只是它必须是每个连接的有效版块)。

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

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

现在,您可以左连接到最小/最大帖子和条目数的预聚合查询...然后再次将其左连接到帖子表但是两次...一次用于第一个帖子(这样您就可以获取初始主题标题、时间和您可能关心的任何其他内容),并再次获取 LAST POST 以获取其时间、主题等……您已经从预聚合查询中获得了该主题的总帖子条目。最后,将最后一个帖子左连接到 users 表,以查看最后发布的人。

我已经测试了它的语法并且它有效,只是无法根据实际数据进行确认。

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 后,请使用触发器执行一些操作...

修正了关于触发影响的想法。

使用最新的 TOPIC ID 更新 kf_Boards 任何帖子都是为相应的板 ID 创建的,因此您以后不必再查找它,只需获取最后一个并使用它运行即可。此外,更新 TOPIC 记录。为该主题的 FIRST POST、LAST POST 和 TOTAL POSTS 设置一个专栏。如果它是该主题的第一篇文章,请使用新 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  
于 2012-11-18T00:11:50.687 回答
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

更新:波纹管适用于您的新查询。

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
于 2012-11-17T04:31:48.533 回答
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检查结果

于 2012-11-17T23:19:40.010 回答