2010-02-09 12 views
11

Ecco il mio problema. Ho una tabella molti-a-molti chiamata 'user_has_personalities'. Nella mia applicazione, gli utenti possono avere molte personalità e una personalità può appartenere a molti utenti.MySQL Many-To-Many Query Problem

La tabella ha due colonne integer, user_id e personality_id.

Quello che devo fare è ottenere tutti gli utenti che hanno almeno tutte le personalità (un set di personalità_idità di dimensioni variabili) che fornisco alla query.

Per un esempio, mi piacerebbe ottenere tutti gli utenti che hanno personalità con ID 4, 5, 7, ma può anche avere alcune altre personalità. Ma ho bisogno che la query funzioni per un numero variabile di id di personalità ricercate, come 4, 5, 7, 9, 10 per un esempio.

Qualche idea?

risposta

6

Questa interrogazione fa il lavoro:

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> 

è necessario fornire un elenco separato da virgole di ID di personalità per <list-of-personality-ids> ed è inoltre necessario fornire il numero di elementi in esimo elist. L'aderenza al tuo esempio, si otterrebbe:

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

questo assicura che si ottiene solo gli utenti che dispongono di tutte queste personalità.

+0

questo dice solo se hanno uno dei personality_id's. La domanda dice esplicitamente TUTTI gli ID. –

+0

@Evan: questa query indica se l'utente ha tutti gli ID. – Quassnoi

+0

Vedo cosa stai facendo. Bello. –

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 
     ) 

valore di offset dovrebbe essere 1 inferiore al numero di elementi nella lista IN.

Se avete la vostra lista personalità in un tavolo dedicato, utilizzare questo:

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 
     ) 

Per farlo funzionare correttamente (e veloce), è necessario disporre di un indice UNIQUE su user_has_personalities (user_id, personality_id) (in questo ordine).

Se si dispone di una tabella users e quasi tutti gli utenti hanno un record in user_has_personalities, poi sostituirlo al posto del DISTINCT interrogazione annidata:

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 per padronanza generale e wizard-ness :) Quassnoi, adoro il tuo blog e ti ho collegato. Gradirei il tuo blog ancora di più se tu avessi aperto commenti su di esso, ma ovviamente è tua la tua prerogativa di lasciarlo così com'è. Ad ogni modo, evviva e continua così :) –

+0

@Roland: tutti gli articoli nella categoria 'Varie» hanno i commenti abilitati. – Quassnoi

+0

ok, abbastanza giusto :) grazie. –

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 

Sarebbe abbastanza facile da generare questa lista programmaticamente, ma non è esattamente così facile come fornire un set. D'altra parte, è efficiente.