2010-02-09 4 views
11

Voici mon problème. J'ai une table many-to-many appelée 'user_has_personalities'. Dans mon application, les utilisateurs peuvent avoir de nombreuses personnalités, et une personnalité peut appartenir à de nombreux utilisateurs.Problème de requête Many-To-Many MySQL

La table comporte deux colonnes entières, user_id et personality_id. Ce que je dois faire est d'obtenir tous les utilisateurs qui ont au moins toutes les personnalités (un ensemble de personality_ids de taille variable) que je fournis à la requête. Par exemple, j'aimerais que tous les utilisateurs ayant des identités 4, 5, 7, puissent avoir d'autres personnalités, par exemple. Mais j'ai besoin que la requête fonctionne pour un nombre variable d'identifiants de personnalité recherchés, comme 4, 5, 7, 9, 10 pour un exemple.

Des idées?

Répondre

6

Cette requête fait le travail:

select user_id 
from user_has_personalities 
where personality_id in (<list-of-personality-ids>) 
group by user_id 
having count(*) = <numer-of-items-in-IN-list> 

Vous devez fournir une liste séparée par des virgules ids de personnalité pour <list-of-personality-ids> et vous devez également fournir le nombre d'articles dans e elist. Coller à votre exemple, vous obtiendrez:

select user_id 
from user_has_personalities 
where personality_id in (4,5,7) 
group by user_id 
having count(*) = 3 

ce vous assure que les utilisateurs qui ont toutes ces personnalités.

+0

cela indique simplement s'ils ont l'un des ID de personnalité. La question dit explicitement tous les identifiants. –

+0

@Evan: cette requête indique si l'utilisateur a tous les identifiants. – Quassnoi

+0

Je vois ce que vous faites. Agréable. –

4
SELECT * 
FROM (
     SELECT DISTINCT user_id 
     FROM user_has_personalities 
     ) uhpo 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM user_has_personalities uhpi 
     WHERE uhpi.user_id = uhpo.user_id 
       AND personality_id IN (4, 5, 6, 9, 10) 
     LIMIT 1 OFFSET 4 
     ) 

valeur de décalage doit être 1 inférieur au nombre d'éléments dans la liste IN.

Si vous avez votre liste de personnalité dans une table dédiée, utilisez ceci:

SELECT * 
FROM (
     SELECT DISTINCT user_id 
     FROM user_has_personalities 
     ) uhpo 
WHERE (
     SELECT COUNT(*) 
     FROM perslist p 
     JOIN user_has_personalities uhpi 
     ON  uhpi.user_id = uhpo.user_id 
       AND uhpi.personality_id = p.id 
     ) = 
     (
     SELECT COUNT(*) 
     FROM perslist 
     ) 

Pour que cela fonctionne correctement (et rapide), vous devez avoir un indice UNIQUE sur user_has_personalities (user_id, personality_id) (dans cet ordre).

Si vous avez une table users et presque tous les utilisateurs ont un enregistrement dans user_has_personalities, puis substituer à la place de la requête imbriquée DISTINCT:

SELECT user_id 
FROM users uhpo 
WHERE (
     SELECT COUNT(*) 
     FROM perslist p 
     JOIN user_has_personalities uhpi 
     ON  uhpi.user_id = uhpo.user_id 
       AND uhpi.personality_id = p.id 
     ) = 
     (
     SELECT COUNT(*) 
     FROM perslist 
     ) 
+0

+1 pour la maîtrise générale et l'assistant-ness :) Quassnoi, j'aime ton blog et je t'ai relié. Je voudrais que votre blog soit encore plus utile si vous pouviez faire des commentaires à ce sujet, mais c'est évidemment votre privilège de le laisser tel quel. Quoi qu'il en soit, applaudissements et continuez :) –

+0

@Roland: tous les articles de la catégorie 'Divers' ont des commentaires activés. – Quassnoi

+0

ok, assez juste :) merci. –

1
SELECT a.user_id 
FROM user_has_personalities a 
JOIN user_has_personalities b ON a.user_id = b.user_id AND b.personality_id = 5 
JOIN user_has_personalities c ON a.user_id = c.user_id AND b.personality_id = 7 
WHERE a.personality_id = 4 

Il serait assez facile de générer cette liste par programmation, mais ce n'est pas aussi simple que de fournir un ensemble. D'un autre côté, c'est efficace.