SQL-запрос, где ВСЕ записи в соединении соответствуют условию?

У меня есть то, что кажется простой проблемой, но не может найти правильное решение через SQL. Я использую postgresql специально.

Возьмите следующее:

SELECT * FROM users INNER JOIN tags ON (tags.user_id = users.id) WHERE tags.name IN ('word1', 'word2')

Это не делает то, что мне нужно. Я хочу найти пользователей, чьи теги ТОЛЬКО включены в список. Если у пользователя есть тег, которого нет в списке, пользователь не должен включаться.

'user1' tags: word1, word2, word3
Теги 'user2': word1
Теги 'user3': word1, word2

Данные: word1 и word2. Я хочу подготовить запрос, который возвращает "user2" и "user3". "user1" исключается, поскольку в нем есть тег, который отсутствует в списке.

Надеюсь, я сделал это ясно. Спасибо за вашу помощь!

Ответ 1

Полагаясь на COUNT (*) = 2, требуется, чтобы в таблице тегов не было дубликатов user_id и имени. Если это произойдет, я поеду туда. В противном случае это должно работать:

SELECT u.* 
FROM users AS u
WHERE u.id NOT IN (
    SELECT DISTINCT user_id FROM tags WHERE name NOT IN ('word1', 'word2')
) AND EXISTS (SELECT user_id FROM tags WHERE user_id = u.id) 

Ответ 2

SELECT  user_id
FROM    users
WHERE   id IN
        (
        SELECT  user_id
        FROM    tags
        )
        AND id NOT IN
        (
        SELECT  user_id
        FROM    tags
        WHERE   name NOT IN ('word1', 'word2')
        )

или

SELECT  u.*
FROM    (
        SELECT  DISTINCT user_id
        FROM    tags
        WHERE   name IN ('word1', 'word2')
        ) t
JOIN    users u
ON      u.id = t.user_id
        AND t.user_id NOT IN
        (
        SELECT  user_id
        FROM    tags
        WHERE   name NOT IN ('word1', 'word2')
        )

Ответ 3

Чтобы получить всех пользователей, у которых нет тега, которого нет в списке, используйте запрос ниже. Возможно, пользователи возвращаются без тега или только одного тега, соответствующего словам, но я понимаю, что это желаемая функциональность.

SELECT
  u.*
FROM
  users u
  LEFT JOIN tags t 
    ON t.user_id = u.userid AND
       t.name NOT IN ('word1', 'word2')
WHERE
  t.user_id IS NULL

Ответ 4

SELECT u.*
FROM users u
INNER JOIN (
  SELECT user_id FROM tags WHERE name IN ('word1', 'word2')
  EXCEPT
  SELECT user_id FROM tags WHERE name NOT IN ('word1', 'word2')
) s ON u.id = s.user_id

Ответ 5

SELECT distinct users.id
FROM users 
INNER JOIN tags ON (tags.user_id = users.id) 
group by users.id 
having count(*) = 2 
and min(tags.name) = 'word1'
and max(tags.name) = 'word2'