我在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
它只显示第一个帖子,以及与该作者关联的所有标签。
我在这里做错了什么?
此外,如果我想计算作者为每个帖子获得的回复数量,将如何做?
答案 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