2010-02-09 19 views
11

Aquí está mi problema. Tengo una tabla de muchos a muchos llamada 'user_has_personalities'. En mi aplicación, los usuarios pueden tener muchas personalidades, y una personalidad puede pertenecer a muchos usuarios.MySQL Many-To-Many Problema de consulta

La tabla tiene dos columnas enteras, user_id y personality_id.

Lo que tengo que hacer es obtener todos los usuarios que tienen al menos todas las personalidades (un conjunto de personality_ids de tamaño variable) que proporciono a la consulta.

Por ejemplo, me gustaría obtener todos los usuarios que tienen personalidades con identificadores 4, 5, 7, pero también pueden tener otras personalidades. Pero necesito que la consulta funcione para un número variable de identificadores de personalidad deseados, como 4, 5, 7, 9, 10 para un ejemplo.

¿Alguna idea?

Respuesta

6

Esta consulta se encarga del trabajo:

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> 

es necesario proporcionar una lista separada por comas de identificadores de personalidad para <list-of-personality-ids> y también es necesario para proporcionar el número de elementos en EList XX. Cumplir con tu ejemplo, se llega a:

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

esto asegura que sólo recibe los usuarios que tienen todas estas personalidades.

+0

esto solo indica si tienen uno de los ID de personalidad. La pregunta dice explícitamente TODOS los ID. –

+0

@Evan: esta consulta indica si el usuario tiene todos los ID. – Quassnoi

+0

Veo lo que estás haciendo. Bonito. –

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 
     ) 

valor de desplazamiento debe ser 1 menor que el número de elementos de la lista IN.

Si usted tiene su lista de la personalidad en una mesa dedicada, utilice esto:

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 
     ) 

Para que esto funcione correctamente (y rápido), es necesario tener un índice de UNIQUE en user_has_personalities (user_id, personality_id) (en este orden).

Si usted tiene una mesa users y casi todos los usuarios tienen un registro en user_has_personalities, luego sustituirlo en lugar de la DISTINCT consulta anidada:

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 para maestría general y wizard-ness :) Quassnoi, me encanta tu blog y te he vinculado. Me gustaría que tu blog aún más si abres comentarios sobre él, pero obviamente es tu prerrogativa dejarlo tal como está. De todos modos, aplausos y sigan así :) –

+0

@Roland: cada artículo en la categoría 'Varios' tiene comentarios habilitados. – Quassnoi

+0

bien, justo :) gracias. –

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 

Sería muy fácil para generar esta lista programáticamente, pero no es tan fácil como suministrar un conjunto. Por otro lado, es eficiente.