2010-07-16 5 views
18

Ich versuche, nach Objekten abzufragen, die ALLEN bestimmten Tags entsprechen.Mysql Join-Abfrage für mehrere "Tags" (Viele-zu-viele-Beziehung), die alle Tags übereinstimmt?

Grundsätzlich möchte ich, dass Benutzer mehr und mehr Tags hinzufügen können, um ihre Suchergebnisse zu filtern oder "einzugrenzen", so wie newegg.com.

Meine Tabellenstruktur ist eine Tabelle von Objekten, eine Tabelle von Tags und eine VIELE: VIELE Beziehungstabelle ObjectsTags. So habe ich eine Abfrage wie JOIN so:

SELECT * FROM Objects 
LEFT OUTER JOIN ObjectsTags ON (Objects.id=ObjectsTags.object_id) 
LEFT OUTER JOIN Tags ON (Tags.id=ObjectsTags.tag_id) 

Ich habe versucht, eine IN-Klausel/Zustand verwendet wird, wie folgt aus:

SELECT * FROM Objects 
LEFT OUTER JOIN ObjectsTags ON (Objects.id=ObjectsTags.object_id) 
LEFT OUTER JOIN Tags ON (Tags.id=ObjectsTags.tag_id) 
WHERE Tags.name IN ('tag1','tag2') 
GROUP BY Objects.id 

Aber ich habe gelernt, dass dies eine Reihe von OPs simuliert, so die weitere Tags Sie fügen der Abfrage die MORE-Ergebnisse hinzu, die Sie erhalten, anstatt dass der Ergebnissatz so eingeengt wird, wie ich es mir erhofft hatte.

Ich versuchte auch mehrere LIKE tun WHERE Bedingungen verknüpft zusammen:

SELECT * FROM Objects 
LEFT OUTER JOIN ObjectsTags ON (Objects.id=ObjectsTags.object_id) 
LEFT OUTER JOIN Tags ON (Tags.id=ObjectsTags.tag_id) 
WHERE Tags.name LIKE 'tag1' 
AND Tags.name LIKE 'tag2' 
GROUP BY Objects.id 

Aber diese lieferte keine Ergebnisse, da, wenn die Ergebnisse zusammengefasst sind der äußere verbundene Tags.name Spalte enthält nur ‚tag1‘ und nicht auch "tag2". Die Ergebniszeile, in der "tag2" gefunden wurde, wird durch GROUPing "versteckt".

Wie kann ich alle Tags übereinstimmen, um den Effekt "Eingrenzen" oder "Drilldown" zu erhalten? Vielen Dank.

Antwort

26

Verwendung:

SELECT * 
    FROM OBJECTS o 
    JOIN OBJECTSTAGS ot ON ot.object_id = o.id 
    JOIN TAGS t ON t.id = ot.tag_id 
    WHERE t.name IN ('tag1','tag2') 
GROUP BY o.id 
    HAVING COUNT(DISTINCT t.name) = 2 

Sie wurden die HAVING-Klausel fehlt.

Es gibt keine Notwendigkeit zu LEFT JOIN, wenn Sie nur die Zeilen möchten, wo beide Tags existieren.

+1

Die DISTINCT ist erforderlich, wenn Sie keinen Primärschlüssel oder eindeutige Integritätsregel/Index für das Spaltenpaar in OBJECTTAGS haben, um sicherzustellen, dass keine Duplikate vorhanden sind (dies wären falsch positive Ergebnisse). –

+0

funktioniert super! Vielen Dank! nie daran gedacht, HAVING auf diese Weise mit Zeilenzählern zu verwenden. FYI für andere Leute: Ich baue meine Abfrage in PHP, so dass die HAVING-Klausel mit der Anzahl der Tags übereinstimmt, nach denen ich suche. Wenn es also ('tag1', 'tag2', 'tag3') ist, suche ich nach HAVING COUNT (DISTINCT t.name) = 3, um sicherzustellen, dass das Ergebnis ALLE Tags hat. – thaddeusmt

+0

Super Antwort. Vielen Dank ! –