使用LEFT JOINS和HAVING不返回所有行

时间:2013-06-02 00:36:08

标签: mysql group-by left-join having

我正在建立一个简单的论坛。我需要显示的是一个包含最新主题的列表,并且每个主题附近都是最新的回复/帖子。类似的东西:

Topic name:         | Last reply:
Topic name 1 here   | 2013-02-01 by username1
Topic name 2 here   | 2013-01-01 by username2
etc.

问题是主题和回复都在同一个名为'pages'的表中。 该表有一个字段模板,当然包含表'模板'的ID。

因此,我只需通过Template.name连接模板并过滤记录“论坛主题”

这将返回所有主题。因为我还需要每个主题的最新回复,所以我决定在同一个模板上使用过滤器做一个子查询但是现在用于记录'论坛回复'并且由Reply.date DESC和LIMIT 1命令,它返回最新的回复这个话题。现在是时候显示我当前的查询适用于包含至少1个回复的主题:

/*----some fields to return----*/
SELECT TopicContent.title, ReplyContent.title, Reply.date, Reply.id, 
(
  /*----subquery to return latest reply id used inside HAVING later on----*/
  SELECT Reply.id
  FROM pages AS Reply
  INNER JOIN templates AS Template ON Reply.template = Template.id
  WHERE Reply.parent_id = Topic.id
  AND Template.name =  'Forum reply'
  ORDER BY Reply.date DESC 
  LIMIT 1
) AS reply_id
FROM pages AS Topic
INNER JOIN templates AS Template ON Topic.template = Template.id
INNER JOIN page_content AS TopicContent ON Topic.id = TopicContent.page
/*----left join used because topic could have zero replies if new----*/
LEFT JOIN pages AS Reply ON Topic.id = Reply.parent_id
LEFT JOIN page_content AS ReplyContent ON Reply.id = ReplyContent.page
WHERE Template.name =  'Forum topic'
HAVING Reply.id = reply_id
/*--------------------------------------------------------------------------------*/
/*----HAVING            | returns not all topics but with correct latest reply----*/
/*----GROUP BY Topic.id | returns topics correctly but incorrect latest reply-----*/

现在最重要的是,如果提交了新的主题,它还不包含任何回复。

所以当我使用HAVING时,它只返回包含至少一个回复的主题。 使用GROUP BY时,它不会返回包含一个或多个回复的主题的最新回复。对于不包含回复的主题,它返回NULL,这很好。

我需要更改JOINS吗?任何想法来解决这个问题。非常感谢!

2 个答案:

答案 0 :(得分:1)

我将MaxId子查询移动到from子句,因为您使用GROUP BY的方式不适用于您尝试执行的操作。在这里,您只需将结果集加入到检索MaxId字段的子查询中。

SELECT
*
FROM 
   pages
   .
   .
   .
   LEFT OUTER JOIN 
   (
   SELECT 
      ReplyId As [MaxId], 
      p.Template
   FROM 
      Pages p INNER JOIN Templates t ON p.Template = t.Id
   WHERE
      TemplateName = 'Forum Reply'
   ORDER BY ReplyDate LIMIT 1
   ) a ON a.Template = pages.Template
WHERE 
   Template.name =  'Forum topic'

或者(如果多个第一次回复具有完全相同的时间,则可以给您多个回复,在这种情况下您可以限制结果)

SELECT
   *
FROM
   pages
   .
   .
   .
   (SELECT
      ReplyId As [MaxiId],
      Template
   FROM
      Pages p INNER JOIN
      Templates t ON p.Template = t.id LEFT OUTER JOIN
      (SELECT 
         p.Template, 
         MIN(ReplyDate) AS [FirstReplyDate]
      FROM      
         Pages p INNER JOIN
         Templates t ON p.Template = t.id
      WHERE
         TemplateName = 'Forum Reply'
      GROUP BY
         p.Template) b ON b.Template = t.id
   WHERE 
      p.ReplyDate = b.FirstReplyDate
   ) a ON a.Template = pages.Template

无论哪种方式,您都应该将第一个回复子查询移动到From子句

答案 1 :(得分:0)

这还不是答案,而是要求海报提供更多信息,但因为评论太长,我会在这里发帖。

从您的查询中,我可以得出结论,您的表格看起来像这样

templates(id, name)
pages(id, parent_id, date, template)
page_contents(page, title)

pages存储帖子的结构,page_contents存储帖子的内容,template确定帖子的类型。我是对的吗?

问题:

  • parent_id的值是多少,如果是主题,而不是回复。由于回复在主题下,因此parent_id应该是主题的ID。但是,主题不在任何主题下,所以基本上,它应该是0或NULL,是不是?
  • 你是否局限于这个数据库设计,从我的角度来看,这个数据库设计不适合论坛,因为要检索像这样简单的东西,你必须INNER JOIN一堆表。