我有一张“Songs”,“Songs_Tags”(与歌曲相关的歌曲)和“Songs_Votes”(与布尔喜欢/不喜欢的歌曲相关)的表格。
我需要使用其标签的GROUP_CONCAT()以及喜欢的数量(真实)和不喜欢(虚假)来检索歌曲。
我的查询是这样的:
SELECT
s.*,
GROUP_CONCAT(st.id_tag) AS tags_ids,
COUNT(CASE WHEN v.vote=1 THEN 1 ELSE NULL END) as votesUp,
COUNT(CASE WHEN v.vote=0 THEN 1 ELSE NULL END) as votesDown,
FROM Songs s
LEFT JOIN Songs_Tags st ON (s.id = st.id_song)
LEFT JOIN Votes v ON (s.id=v.id_song)
GROUP BY s.id
ORDER BY id DESC
问题是,当一首歌有超过1个标签时,它会被多次返回,所以当我执行COUNT()时,它会返回更多结果。
我能想到的最好的解决方案是,在GROUP BY之后是否可以进行最后的LEFT JOIN(所以现在每首歌只有一个条目)。然后我需要另一个GROUP BY m.id。
有没有办法实现这一目标?我需要使用子查询吗?
答案 0 :(得分:5)
到目前为止已经有了一些很好的答案,但我会采用与你原来描述的方法略有不同的方法
SELECT
songsWithTags.*,
COALESCE(SUM(v.vote),0) AS votesUp,
COALESCE(SUM(1-v.vote),0) AS votesDown
FROM (
SELECT
s.*,
COLLATE(GROUP_CONCAT(st.id_tag),'') AS tags_ids
FROM Songs s
LEFT JOIN Songs_Tags st
ON st.id_song = s.id
GROUP BY s.id
) AS songsWithTags
LEFT JOIN Votes v
ON songsWithTags.id = v.id_song
GROUP BY songsWithTags.id DESC
在此,子查询负责将带有标签的歌曲整理为每首歌曲1行。然后将其加入投票。我还选择简单地总结v.votes列,因为你已经指出它是1或0,因此SUM(v.votes)将加起来1 + 1 + 1 + 0 + 0 = 3中的3个是upvotes,而SUM(1-v.vote)将总和0 + 0 + 0 + 1 + 1 = 2中的5个是downvotes。
如果你有一个带有列(id_song,vote)的投票索引,那么该索引将用于此,所以它甚至不会命中表。同样,如果您在Songs_Tags上有一个带有(id_song,id_tag)的索引,那么该表将不会被查询命中。
编辑使用计数
添加解决方案SELECT
songsWithTags.*,
COUNT(CASE WHEN v.vote=1 THEN 1 END) as votesUp,
COUNT(CASE WHEN v.vote=0 THEN 1 END) as votesDown
FROM (
SELECT
s.*,
COLLATE(GROUP_CONCAT(st.id_tag),'') AS tags_ids
FROM Songs s
LEFT JOIN Songs_Tags st
ON st.id_song = s.id
GROUP BY s.id
) AS songsWithTags
LEFT JOIN Votes v
ON songsWithTags.id = v.id_song
GROUP BY songsWithTags.id DESC
答案 1 :(得分:3)
试试这个:
SELECT
s.*,
GROUP_CONCAT(DISTINCT st.id_tag) AS tags_ids,
COUNT(DISTINCT CASE WHEN v.vote=1 THEN id_vote ELSE NULL END) AS votesUp,
COUNT(DISTINCT CASE WHEN v.vote=0 THEN id_vote ELSE NULL END) AS votesDown
FROM Songs s
LEFT JOIN Songs_Tags st ON (s.id = st.id_song)
LEFT JOIN Votes v ON (s.id=v.id_song)
GROUP BY s.id
ORDER BY id DESC
答案 2 :(得分:2)
您的代码会产生迷你笛卡尔积,因为您在1-to-many
关系中进行了两次连接,而1
表位于两个连接的同一侧。
使用分组转换为2个子查询,然后加入:
SELECT
s.*,
COALESCE(st.tags_ids, '') AS tags_ids,
COALESCE(v.votesUp, 0) AS votesUp,
COALESCE(v.votesDown, 0) AS votesDown
FROM
Songs AS s
LEFT JOIN
( SELECT
id_song,
GROUP_CONCAT(id_tag) AS tags_ids
FROM Songs_Tags
GROUP BY id_song
) AS st
ON s.id = st.id_song
LEFT JOIN
( SELECT
id_song,
COUNT(CASE WHEN v.vote=1 THEN id_vote END) AS votesUp,
COUNT(CASE WHEN v.vote=0 THEN id_vote END) AS votesDown
FROM Votes
GROUP BY id_song
) AS v
ON s.id = v.id_song
ORDER BY s.id DESC