2011-01-21 3 views
10

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

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

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

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

'User1' теги: word1, word2, word3
метки 'user2': Слово1
метки 'USER3': word1, WORD2

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

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

+2

возможно дубликат [? SQL, как соответствовать ALL] (http://stackoverflow.com/questions/4763143/sql-how-to-match -all) –

+0

Разве вы не спросили об этом –

+0

Должны ли быть возвращены пользователи, у которых нет тегов? – Quassnoi

ответ

6

Опираясь на 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

Это * может * включать пользователей без тегов. –

+0

@Andriy M: Вы правы. Теперь я обновил свой ответ для учетной записи. Спасибо, что указали. – nybbler

+0

'DISTINCT' просто лишние накладные расходы здесь ... – JNK

1
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') 
     ) 
+0

Это вернет всех пользователей, которые соответствуют обоим словам, не проверяя, не соответствуют ли они другим словам. Это не отвечает на вопрос. – GolezTrol

+0

@GolezTrol: получилось неправильно, спасибо за указание. – Quassnoi

0

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

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 
0
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 
0
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' 
+1

Не могли бы вы дать некоторое объяснение, почему ваш запрос лучше, чем то, что уже было опубликовано? – Theresa