复杂(ish)加入问题:

时间:2011-04-12 17:23:47

标签: mysql sql join

我在mySQL数据库中有以下表格(nb:这些是汇总的,因此它们与此问题相关,某些列已被省略)。

author (id, username, password etc.)
thread (id, title, content)
tag (id, name)
reply (id, content)
thread_replies (thread_id, reply_id)
author_replies (author_id, reply_id)
thread_tags (thread_id, tag_id)
author_threads (author_id, thread_id)

现在要通过某个作者获取线程,我通常会这样做:

SELECT thread.title, thread.id AS thread_id, thread.content, author.username, author.id AS author_id
FROM thread
JOIN author_threads ON thread.id = author_threads.thread_id
JOIN author ON author_threads.author_id = author.id
WHERE author.id = '12'

这很好,但是当我尝试获取与这些线程关联的标记时:

SELECT thread.title, thread.id AS thread_id, thread.content, author.username, author.id AS author_id, GROUP_CONCAT( DISTINCT tag.name
ORDER BY tag.name DESC
SEPARATOR ',' ) AS tags
FROM thread
JOIN thread_tags ON thread.id = thread_tags.thread_id
JOIN tag ON thread_tags.tag_id = tag.id
JOIN author_threads ON thread.id = author_threads.thread_id
JOIN author ON author_threads.author_id = author.id
WHERE author.id = '12'
LIMIT 0 , 30

它只显示第一个帖子,以及与该作者关联的所有标签。

我在这里做错了什么?

此外,如果我想计算作者为每个帖子获得的回复数量,将如何做?

2 个答案:

答案 0 :(得分:2)

由于您在查询中使用了汇总(GROUP_CONCAT),因此您的查询正在进行分组。由于您没有group by子句,因此您的组是整个结果集(因此查看作者使用的每个标记)。因为MySQL允许在分组语句中使用非分组列,所以您没有收到错误,但是您没有获得所需的查询。

要检索正确的结果,您需要将查询分组到thread.id

select
    thread.title, 
    thread.id as thread_id, 
    thread.content, 
    author.username, 
    author.id as author_id, 
    group_concat(distinct tag.name order by tag.name desc separator ',') as tags

from thread

join thread_tags ON thread.id = thread_tags.thread_id
join tag ON thread_tags.tag_id = tag.id
join author_threads ON thread.id = author_threads.thread_id
join author ON author_threads.author_id = author.id

where author.id = '12'

group by thread.id

limit 0 , 30

这应该适用于MySQL,虽然它不符合ANSI标准的SQL,因为你在select子句中使用了非分组列而没有任何聚合。您可以保持原样,也可以编写更符合要求的SQL,并在max以外的所有列周围使用thread.id之类的内容。这看起来不太漂亮,但它会合规。

SELECT 
    max(thread.title) as title, 
    thread.id as thread_id, 
    max(thread.content) as content, 
    max(author.username) as username, 
    max(author.id) as author_id, 
    group_concat(distinct tag.name order by tag.name desc separator ',') as tags

from thread

join thread_tags ON thread.id = thread_tags.thread_id
join tag ON thread_tags.tag_id = tag.id
join author_threads ON thread.id = author_threads.thread_id
join author ON author_threads.author_id = author.id

where author.id = '12'

group by thread.id

LIMIT 0 , 30

回复计数

上述查询(以及您的原始查询)仅适用于 ,用于检索标记列表。您可以编写一个等效的查询来检索回复计数(假设回复不是嵌套的,在这种情况下,您将不得不使用MySQL提供的任何递归查询功能,我不熟悉),但要检索两者在单个查询中需要子查询:

select
    thread.title, 
    thread.id as thread_id, 
    thread.content, 
    author.username, 
    author.id, 
    (select group_concat(distinct tag.name order by tag.name separator ',')

    from thread_tags

    join tag on tag.id = thread_tags.tag_id 

    where thread_tags.thread_id = thread.id) as tags,
    (select count(1) from thread_replies where thread_id = thread.id) as reply_count

from thread

join author_threads ON thread.id = author_threads.thread_id
join author ON author_threads.author_id = author.id

where author.id = '12'

LIMIT 0 , 30

我已从此查询中删除group by,因为我们的聚合已移至子选择中,这意味着外部查询不再分组。

答案 1 :(得分:0)

我会首先在内部预先查询线程和标记信息......然后,这将已经有作者和有效线程加入到你需要的任何其他内容中......

如果您想要应用限制,请将其放在INNER“PREQUERY”中,因为这将成为加入外层表的基础...否则,您将得到100或1000的内部查询加上条目加入其他表并切成30条记录......让IT停在30,你就完成了......

在限制返回的条目时,您可能还希望通过大多数当前线程进行排序。

select STRAIGHT_JOIN
      PreQuery.*,
      Author.username,
      Thread.title,
      Thread.Content

   from 
      ( select STRAIGHT_JOIN
              author_threads.author_id,
              author_threads.thread_id,
              group_concat(distinct tag.name order by tag.name desc separator ',') as tags
           from 
              author_threads
                 join thread_tags
                    on author_threads.thread_id = thread_tags.thread_id
                    join tag
                       on thread_tags.tag_id = tag.id
           where
              author_threads.author_id = '12'
           group by
              author_threads.author_id,
              author_threads.thread_id
           limit 0, 30 ) PreQuery

      join author
         on PreQuery.Author_ID = author.id

      join thread
         on PreQuery.Thread_id = thread.id