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'