Ada beberapa jawaban yang bagus sejauh ini, tetapi saya akan mengadopsi metode yang sedikit berbeda yang sangat mirip dengan apa yang Anda gambarkan pada awalnya
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
Dalam hal ini subquery bertanggung jawab untuk menyusun lagu dengan tag menjadi 1 baris per basis lagu. Ini kemudian digabungkan ke Votes sesudahnya. Saya juga memilih untuk menjumlahkan kolom v.votes seperti yang Anda tunjukkan itu adalah 1 atau 0 dan oleh karena itu SUM(v.votes) akan bertambah 1+1+1+0+0 =3 dari 5 adalah upvotes, sedangkan SUM(1-v.vote) akan menjumlahkan 0+0+0+1+1 =2 dari 5 adalah downvote.
Jika Anda memiliki indeks pada suara dengan kolom (id_song,vote) maka indeks itu akan digunakan untuk ini sehingga tidak akan mencapai meja. Demikian juga jika Anda memiliki indeks pada Songs_Tags dengan (id_song,id_tag) maka tabel tersebut tidak akan terkena kueri.
edit menambahkan solusi menggunakan 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