Досега има някои добри отговори, но бих възприел малко по-различен метод, доста подобен на този, който описахте първоначално
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 от 5 са гласа „за“, докато SUM(1-v.vote) ще сумира 0+0+0+1+1 =2 от 5 са отрицателни гласове.
Ако сте имали индекс на гласове с колоните (id_song,vote), тогава този индекс ще се използва за това, така че дори да не се удари в таблицата. По същия начин, ако имате индекс на Songs_Tags с (id_song,id_tag), тогава тази таблица няма да бъде засегната от заявката.
редактиране добавен разтвор с помощта на count
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