2012-04-30 16 views
6

Ho tre tabelle MySQL: foto, tag e tag photos e m: n relazione tra foto e tag.Seleziona foto con più tag

Photos:  id | filename | ... 
Tags:  id | name 
Tagsphotos: photo | tag 

voglio selezionare tutte le foto con questa condizione:

(tagged as "dirty" AND tagged as "road") AND (tagged as "light.front" OR tagged as "light.side") AND (tagged as "perspective.two-point") 

... il che significa che voglio trovare tutte le immagini con strada sterrata, in prospettiva a due punti e sia con lato o luce anteriore.

Come posso farlo? Grazie.

risposta

3

penso che stai andando ad avere per partecipare al tavolo tag per le foto tavolo quattro volte ... abbastanza brutti.

SELECT Photos.* 
FROM 
    Photos 
    JOIN (
    Tagsphotos JOIN Tags ON (Tags.id = Tagsphotos.tag) 
) t1 ON (t1.photo = Photos.id) 
    JOIN (
    Tagsphotos JOIN Tags ON (Tags.id = Tagsphotos.tag) 
) t2 ON (t2.photo = Photos.id) 
    JOIN (
    Tagsphotos JOIN Tags ON (Tags.id = Tagsphotos.tag) 
) t3 ON (t3.photo = Photos.id) 
    JOIN (
    Tagsphotos JOIN Tags ON (Tags.id = Tagsphotos.tag) 
) t4 ON (t4.photo = Photos.id) 
WHERE 
     (t1.name = 'dirty' AND t2.name = 'road') 
    AND (t3.name = 'light.front' OR t3.name = 'light.side') 
    AND (t4.name = 'perspective.two-point') 

Sottointerrogazioni sarebbe probabilmente più veloce:

SELECT * 
FROM Photos 
WHERE 
    Photos.id IN (
    SELECT Tagspohotos.photo 
    FROM Tagsphotos JOIN Tags ON (Tags.id = Tagsphotos.tag) 
    WHERE Tags.name = 'dirty' 
) 
    AND Photos.id IN (
    SELECT Tagspohotos.photo 
    FROM Tagsphotos JOIN Tags ON (Tags.id = Tagsphotos.tag) 
    WHERE Tags.name = 'road' 
) 
    AND Photos.id IN (
    SELECT Tagspohotos.photo 
    FROM Tagsphotos JOIN Tags ON (Tags.id = Tagsphotos.tag) 
    WHERE Tags.name = 'light.front' OR Tags.name = 'light.side' 
) 
    AND Photos.id IN (
    SELECT Tagspohotos.photo 
    FROM Tagsphotos JOIN Tags ON (Tags.id = Tagsphotos.tag) 
    WHERE Tags.name = 'perspective.two-point' 
) 
+0

Grazie. La soluzione con sottoquery funziona bene. –

0

Supponendo di foto e tag colonne sono gli ID:

select p.* from Photos p, Tags t, Tagsphotos tp 
where p.id = tp.photo and t.id = tp.tag 
and t.tagged in ("dirty", "road", "perspective.two-point", "light.front") 
group by p.photo 
having count(distinct t.tagged) = 4 

UNION 

select p.* from Photos p, Tags t, Tagsphotos tp 
where p.id = tp.photo and t.id = tp.tag 
and t.tagged in ("dirty", "road", "perspective.two-point", "light.side") 
group by p.photo 
having count(distinct t.tagged) = 4 
+1

'DOVE t.tagged = x E t.tagged = y '? Come mai restituirà un risultato se 'x <> y'? – eggyal

+1

Questa è stata la mia prima idea e, naturalmente, non funziona. –

+0

Davvero! Lo cambierò :) –

1

Scuse, non mi rendevo conto che si stava utilizzando MySQL - risposta aggiornato indicato di seguito.

Supponendo che ogni tag può essere specificato solo una volta per ogni foto (ad esempio una foto non può essere etichettato come 'sporca' più volte):

SELECT  P.id, 
      P.[filename] 
FROM  Photos P 
INNER JOIN Tagsphotos TP ON TP.photo = P.id 
INNER JOIN Tags T ON TP.tag = T.id 
INNER JOIN (
     SELECT 'dirty' name, 
       10 [weight] 
     UNION 
     SELECT 'road' name, 
       10 [weight] 
     UNION 
     SELECT 'perspective.two-point' name, 
       10 [weight] 
     UNION 
     SELECT 'light.front' name, 
       1 [weight] 
     UNION 
     SELECT 'light.side' name, 
       1 [weight] 
) R ON T.name = R.name 
GROUP BY P.id, 
      P.[filename] 
HAVING SUM(R.[weight]) >= 31 
+0

aggiornato. CTE rimosso. – weenoid

+0

Sto amando questo. +1 – eggyal

Problemi correlati