ЛЕВЫЙ РЕЙТИНГ после GROUP BY? - PullRequest
       30

ЛЕВЫЙ РЕЙТИНГ после GROUP BY?

8 голосов
/ 22 февраля 2012

У меня есть таблица «Songs», «Songs_Tags» (относящиеся к песням с тегами) и «Songs_Votes» (относящиеся к песням с булевыми типами / неприязнью).

Мне нужно получить песни с помощью GROUP_CONCAT () из его тегов, а также количества лайков (true) и дислайков (false).

Мой запрос выглядит примерно так:

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 (), она возвращает больше результатов.

Лучшее решение, которое я мог бы придумать, - это если бы было возможно выполнить последнее LEFT JOIN после GROUP BY (так что теперь для каждой песни будет только одна запись). Тогда мне понадобится еще один GROUP BY m.id.

Есть ли способ сделать это? Нужно ли использовать подзапрос?

Ответы [ 3 ]

5 голосов
/ 22 февраля 2012

До сих пор было несколько хороших ответов, но я бы применил немного другой метод, весьма похожий на тот, который вы описали изначально

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, и, следовательно, сумма (v.votes) сложит 1 + 1 + 1 + 0 + 0 = 3 из 5 являются повышающими,в то время как SUM (1-v.vote) будет суммировать 0 + 0 + 0 + 1 + 1 = 2 из 5 являются отрицательными.

Если бы у вас был индекс голосов со столбцами (id_song, голосования), тогда этот индекс будет использоваться для этого, чтобы он даже не попал в таблицу.Аналогично, если у вас есть индекс для Songs_Tags с (id_song, id_tag), тогда эта таблица не будет обработана запросом.

edit добавлено решение с использованием счетчика

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
3 голосов
/ 22 февраля 2012

Ваш код приводит к мини-декартовому продукту, потому что вы выполняете два объединения в отношениях 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
3 голосов
/ 22 февраля 2012

Попробуйте это:

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
...