C'est un cas de division relationnelle . Nous avons rassemblé un arsenal de techniques sous cette question connexe :
La difficulté particulière est d'exclure des utilisateurs supplémentaires. Il existe essentiellement 4 techniques.
Je suggère 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)
);
Les deux requêtes ne le font pas dépendent d'un UNIQUE
contrainte pour (conversation_id, user_id)
, qui peut ou non être en place. Cela signifie que la requête fonctionne même si user_id
32 (ou 3) est répertorié plus d'une fois pour la même conversation. Vous le feriez obtenir des lignes en double dans le résultat, cependant, et besoin d'appliquer DISTINCT
ou GROUP BY
.
La seule condition est celle que vous avez formulée :
Requête auditée
La requête que vous avez liée dans le commentaire ne fonctionnerait pas. Vous avez oublié d'exclure d'autres participants. Devrait être quelque chose comme :
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)
);
Ce qui est similaire aux deux autres requêtes, sauf qu'il ne renverra pas plusieurs lignes si user_id = 3
est lié plusieurs fois.