Este é um caso de divisão relacional . Reunimos um arsenal de técnicas sob esta questão relacionada:
A dificuldade especial é excluir usuários adicionais. Existem basicamente 4 técnicas.
Sugiro
LEFT JOIN
/ IS NULL
:SELECT cu1.conversation_id
FROM conversation_user cu1
JOIN conversation_user cu2 USING (conversation_id)
LEFT JOIN conversation_user cu3 ON cu3.conversation_id = cu1.conversation_id
AND cu3.user_id NOT IN (3,32)
WHERE cu1.user_id = 32
AND cu2.user_id = 3
AND cu3.conversation_id IS NULL;
Ou
NOT EXISTS
:SELECT cu1.conversation_id
FROM conversation_user cu1
JOIN conversation_user cu2 USING (conversation_id)
WHERE cu1.user_id = 32
AND cu2.user_id = 3
AND NOT EXISTS (
SELECT 1
FROM conversation_user cu3
WHERE cu3.conversation_id = cu1.conversation_id
AND cu3.user_id NOT IN (3,32)
);
Ambas as consultas não dependem de um
UNIQUE
restrição para (conversation_id, user_id)
, que pode ou não estar em vigor. Ou seja, a consulta funciona mesmo se user_id
32 (ou 3) é listado mais de uma vez para a mesma conversa. Você faria obter linhas duplicadas no resultado, porém, e precisa aplicar DISTINCT
ou GROUP BY
.A única condição é a que você formulou:
Consulta auditada
A consulta que você vinculou no comentário não funcionaria. Você esqueceu de excluir outros participantes. Deve ser algo como:
SELECT * -- or whatever you want to return
FROM conversation_user cu1
WHERE cu1.user_id = 32
AND EXISTS (
SELECT 1
FROM conversation_user cu2
WHERE cu2.conversation_id = cu1.conversation_id
AND cu2.user_id = 3
)
AND NOT EXISTS (
SELECT 1
FROM conversation_user cu3
WHERE cu3.conversation_id = cu1.conversation_id
AND cu3.user_id NOT IN (3,32)
);
Que é semelhante às outras duas consultas, exceto que não retornará várias linhas se
user_id = 3
está vinculado várias vezes.