2010-06-21 13 views
5

dicono che c'è tale tabella:Mysql: come selezionare gruppi con determinati valori?

mysql> SELECT * FROM tags; 
+---------+--------+ 
| post_id | tag_id | 
+---------+--------+ 
|  1 |  2 | 
|  1 |  3 | 
|  1 |  1 | 
|  2 |  1 | 
|  2 |  2 | 
+---------+--------+ 
5 rows in set (0.00 sec) 

I nomi dei campi sono abbastanza auto-esplicativo. Voglio selezionare post_id s che hanno sia 1 che 3 tag_id s, quindi in questo esempio è solo 1. Ho pensato a qualcosa come SELECT post_id FROM tags GROUP BY post_id HAVING ... Dopo aver voluto elencare tag_id s che sono presenti in questo gruppo. Come lo faccio?

risposta

5

Se post_id e tag_id entrambi hanno un vincolo unico, che dovrebbe funzionare anche:

SELECT post_id 
FROM tags 
WHERE tag_id = 1 OR tag_id = 3 
GROUP BY post_id 
HAVING count(*) = 2; 

Se non ci sono vincoli univoci cercano:

SELECT DISTINCT post_id 
FROM tags 
WHERE tag_id = 1 OR tag_id = 3 
GROUP BY post_id 
HAVING count(DISTINCT tag_id) = 2; 
+0

Grazie, ho deciso di andare con 'SELECT post_id FROM tag WHERE tag_id IN (1,3) GROUP BY post_id HAVING COUNT (1) = 2;', che scala bene ed è il più vicino alla tua soluzione – htf

1

Ho fatto alcune supposizioni sugli altri tavoli. (Vale a dire che si dispone di un tavolo per i messaggi che ho chiamato posts e uno con tag_id come il PK che ho chiamato tag_table per evitare una nameclash con la tabella di messaggi/tag che posso vedere che già chiama tags)

È vuoi i post in cui non esiste un tag nell'elenco {1,3} per il quale non esiste un record corrispondente con il post_id/tag_id corrispondente in modo da poter utilizzare un doppio costrutto NON ESISTE come di seguito.

SELECT post_id 
FROM posts p 
WHERE NOT EXISTS 
    (SELECT * FROM tag_table tt 
    WHERE tag_id IN (1,3) 
    AND NOT EXISTS 
     (SELECT * FROM tags t 
     WHERE t.tag_id = tt.tag_id and 
     p.post_id = t.post_id)   
    ) 

Un altro approccio alternativo consiste nell'utilizzare Raggruppamento e conteggio. A review of approaches to this problem is here.

+0

+1 per il link più bello) – htf

2

Si potrebbe provare un self join (N tag_id -> N join) ma probabilmente non è veloce

SELECT t1.post_id 
FROM tags t1 INNER JOIN tags t2 ON t1.post_id = t2.post_id 
WHERE t1.tag_id = 1 AND t2.tag_id = 3 
+0

+1 ma aggiungerò una selezione distinta alla tua richiesta. – Fred

+1

Tuttavia non scala effettivamente per una quantità arbitraria di tag. –

0

Come su

SELECT * 
FROM tags 
WHERE post_id in 
    (SELECT post_id AS pid 
    FROM tags 
    WHERE 1 IN (SELECT tag_id FROM tags WHERE post_id = pid) 
    AND 3 IN (SELECT tag_id FROM tags WHERE post_id = pid) 
); 
0

DOVE versione di @ soluzione Keeper

SELECT DISTINCT t1.post_id 
FROM tags t1, tags t2 
WHERE 
    t1.post_id = t2.post_id AND 
    t1.tag_id = 1 AND t2.tag_id = 3 
+0

Non pensare che la seconda clausola del tuo OR sia utile. – Fred

+0

@Fred hmm .. sì, hai ragione - aggiornerà – Amarghosh

1
SELECT post_id 
    FROM (SELECT post_id, 
       count(tag_id) AS counter 
      FROM tags 
      WHERE tag_id IN (1,3) 
      GROUP BY post_id 
     ) 
WHERE counter = 2 

Utilizzare GROUP_CONCAT() per la s econda parte della tua domanda

SELECT post_id, 
     GROUP_CONCAT(tag_id ORDER BY tag_id ASC SEPARATOR ',') 
    FROM tags