Выберите строки, которые имеют несколько значений в соединительной таблице

У меня есть три таблицы:

posts (id, content)
posts_tags (posts_id, tags_id)
tags (id, tag)

Как выбрать все сообщения, которые имеют (как минимум) 2 определенных тега (скажем, теги с идентификаторами 1 и 2)?

Например, таблица сообщений:

  id   content   
 ---- ------- 
  1    post1  
  2    post2  
  3    post3  

таблица тегов:

  id   tag   
 ---- ------ 
  1    tag1  
  2    tag2  
  3    tag3  

таблица posts_tags:

  posts_id   tags_id  
 ---------- --------- 
  1          1        
  1          2        
  2          1        
  3          1        
  3          2        
  3          3        

Затем я ожидаю следующий результат:

  id   content  
 ---- --------- 
  1    post1    
  3    post3   

Публикация с идентификатором 3 (поскольку она имеет теги 1, 2 и 3) и публикация с идентификатором 1 (поскольку она имеет теги с идентификатором 1 и 2), но не публикация 2, поскольку у нее нет тега с идентификатором 2.

Предположим, я не могу изменить структуру таблицы.

2 ответа

Решение

Нашел ответ на свой вопрос:

SELECT posts.*
FROM posts
INNER JOIN posts_tags ON posts_tags.posts_id = posts.id
INNER JOIN tags ON tags.id = posts_tags.tags_id
WHERE tags.id IN (1, 2)
GROUP BY posts.id
HAVING COUNT(*) > 1
SELECT * 
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id IN (1,2);


SELECT * 
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id = 1 OR pt.tags_id = 2;


SELECT * 
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id = 1 AND pt.tags_id = 2;

РЕДАКТИРОВАТЬ: быстро и грязно

WITH j AS (
SELECT  pt.posts_id AS post, 
    p.content AS content, 
    STRING_AGG(pt.tags_id::TEXT,',') AS agg
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
GROUP BY pt.posts_id, p.content
)
SELECT post,content
FROM j
WHERE STRING_TO_ARRAY(agg,',') @> ('{2,1}'::TEXT[])
Другие вопросы по тегам