Объединить две выборки из таблицы М:М с учётом count

Есть три таблицы: арты, теги и связующая таблица для проставления тегов для каждого арта.

main_tags — все теги;

arts — арты;

tag_to_art — связь тегов и артов;

Часть данных из таблиц:

SELECT id, ru FROM main_tags ORDER BY id ASC LIMIT 5;

 id |       ru
----+-----------------
  2 | мужчина
  3 | девушка
  4 | длинные волосы
  5 | короткие волосы
  6 | на улице
SELECT id FROM arts;

 id
----
  4
  5
SELECT * FROM tag_to_art;

         tag          | art
----------------------+-----
 девушка              |   5
 девушка              |   4
 длинные волосы       |   5
 на улице             |   4

Выходит, что на арте с id 4 два тега: девушка и на улице, а на арте 5 - девушка и длинные волосы.

Вот так можно найти все теги, которые проставлены на отдельный арт по его id:

SELECT main_tags.id, tag_to_art.tag
FROM main_tags
JOIN tag_to_art ON main_tags.ru = tag_to_art.tag
WHERE tag_to_art.art = '4';

 id |         tag
----+----------------------
  6 | на улице
  3 | девушка

Таким образом можно найти сколько раз был привязан тег к разным артам:

SELECT COUNT(tag) FROM tag_to_art
WHERE tag = 'девушка';

 count
-------
     2

Вопрос: Как мне объединить оба запроса, чтобы получить на выходе такой вид:

 id |         tag          | count
----+----------------------+-------
  4 | длинные волосы       |     1
  3 | девушка              |     2

Моя попытка:

    art_id = 4 # сюда приходит любой id по запросу

    cur.execute(f"""SELECT main_tags.id, tag_to_art.tag, COUNT(tag_to_art.tag) AS count
                FROM main_tags
                JOIN tag_to_art ON main_tags.ru = tag_to_art.tag
                WHERE tag_to_art.art = '{art_id}'
                GROUP BY main_tags.id, tag_to_art.tag
                ORDER BY count, date DESC
                """)

    data = cur.fetchall()

Увы, но таким образом почти всё верно, но подсчитывает только количество использования тега на конкретном арте, а не во всей таблице.

 id |         tag          | count
----+----------------------+-------
  4 | длинные волосы       |     1
  3 | девушка              |     1 # но здесь должно быть 2, оно подсчитало только на одном арте

Мне необходимо или используя один запрос (можно с подзапросами), или инструментами Python сделать на выходе необходимый вид.


Ответы (1 шт):

Автор решения: Akina
SELECT t1.id, t2.tag, COUNT(*) AS "count"
FROM main_tags t1
JOIN tag_to_art t2 ON t1.ru = t2.tag
JOIN arts t3 ON t2.art = t3.id
GROUP BY t1.id, t2.tag
HAVING MAX((t2.art = 4)::INT) = 1;
id tag count
6 на улице 1
3 девушка 2
SELECT t1.id, t2.tag, COUNT(*) AS "count"
FROM main_tags t1
JOIN tag_to_art t2 ON t1.ru = t2.tag
JOIN arts t3 ON t2.art = t3.id
GROUP BY t1.id, t2.tag
HAVING MAX((t2.art = 5)::INT) = 1;
id tag count
3 девушка 2
4 длинные волосы 1

fiddle

→ Ссылка