2009-08-25 10 views
4

ich zwei Tabellen in SQL haben, eine mit einem Projekt und einer mit Kategorien, die Projekte gehören, dh die würde in etwa wie folgt aussehen JOIN:SQL-Filterung durch mehrere Elemente in der gleichen Spalte

Project | Category 
--------+--------- 
    Foo | Apple 
    Foo | Banana 
    Foo | Carrot 
    Bar | Apple 
    Bar | Carrot 
    Qux | Apple 
    Qux | Banana 

(Die Zeichenfolgen werden natürlich durch IDs aus einem höheren Normalformat ersetzt. Aber Sie erhalten den Punkt hier.)

Was ich tun möchte, ist Filtern ermöglichen, so dass Benutzer eine beliebige Anzahl von Kategorien auswählen können und die Ergebnisse nach Elementen gefiltert werden Mitglieder aller ausgewählten Kategorien. Wenn ein Benutzer beispielsweise die Kategorien "Apple" und "Banana" auswählt, werden die Projekte "Foo" und "Qux" angezeigt. Wenn ein Benutzer die Kategorien "Apple", "Banana" und "Carrot" auswählt, wird nur das "Foo" -Projekt angezeigt.

Das erste, was ich ausprobiert habe, war ein einfaches SELECT DISTINCT-Projekt VON ... WHERE Kategorie = 'Apple' UND Kategorie = 'Banane', aber das funktioniert natürlich nicht, da Apple und Banana in derselben Spalte erscheinen in zwei verschiedenen Reihen für jedes gemeinsame Projekt.

GROUP BY und HAVING machen mir nichts, also sag mir: Gibt es einen offensichtlichen Weg, dies zu tun, den ich vermisse, oder ist es wirklich so kompliziert, dass ich darauf zurückgreifen muss? rekursive Joins?

Dies ist übrigens in PostgreSQL, aber natürlich ist Standard-SQL-Code immer vorzuziehen, wenn möglich.

+0

Werfen Sie einen Blick auf diese Frage: http://stackoverflow.com/questions/1242223/mysql-find-rows-matching-all-rows-from-joined-table Nicht ein genaues Duplikat, aber ziemlich nah – ChssPly76

Antwort

6

diesen Artikel in meinem Blog für die Leistung Details:


Die Lösung unter:

  • Arbeiten auf einer beliebigen Anzahl von Kategorien

  • Ist effizienter, dass COUNT und GROUP BY, da es die Existenz eines Projekts/Kategorie Paar genau einmal überprüft, ohne zu zählen.

­

SELECT * 
FROM (
     SELECT DISTINCT Project 
     FROM mytable 
     ) mo 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM (
       SELECT 'Apple' AS Category 
       UNION ALL 
       SELECT 'Banana' 
       UNION ALL 
       SELECT 'Carrot' 
       ) list 
     WHERE NOT EXISTS 
       (
       SELECT NULL 
       FROM mytable mii 
       WHERE mii.Project = mo.Project 
         AND mii.Category = list.Category 
       ) 
     ) 
+0

Manchmal macht SQL mein Gehirn weh, und das ist eine dieser Zeiten - der Code funktioniert großartig, und es ist sehr effizient, aber ich hätte nie gedacht, zwei "Select Where not exists" -Abfragen zu machen. Vielen Dank! – Chris

+0

'@ Chris': Das formuliert einen prozeduralen Ansatz in festen Mengen. Wenn Sie sich diese Abfrage genauer ansehen, tun sie genau so, wie es ein Programmierer tun würde: "Nehmen Sie für jedes Projekt jede Kategorie und prüfen Sie, ob sie in diesem Projekt vorhanden ist", aber ohne Cursor oder Schleifen. Es ist eine gute Illustration zu diesem Artikel, den ich vor einiger Zeit geschrieben habe: http://explainextended.com/2009/07/12/double-thinking-in-sql/ – Quassnoi

4

Da ein Projekt nur einmal in einer Kategorie sein kann, können wir COUNT verwenden, diesen Stunt abziehen:

SELECT project, COUNT(category) AS cat_count 
    FROM /* your join */ 
    WHERE category IN ('apple', 'banana') 
    GROUP BY project 
    HAVING cat_count = 2 

Ein Projekt mit einer Gruppe von nur Apfel oder Banane wird eine Zählung von 1 erhalten, und scheitern Sie daher die Klausel. Nur ein Projekt mit beiden Kategorien erhält eine Anzahl von 2.

Wenn Sie aus irgendeinem Grund doppelte Kategorien haben, können Sie etwas wie COUNT(DISTINCT category) verwenden. COUNT(*) sollte ebenfalls funktionieren und unterscheidet sich nur dann, wenn die Kategorie null sein kann.

+0

Das OP möchte, wo ein Projekt BEIDES/alle Kategorien hat, auch nicht. Das Zählen wäre auch dann gültig, wenn ein Projekt zwei Instanzen von "Apple" aufweist. –

+0

Das ist, was die Having-Klausel erreicht, und der Hinweis über distinct deckt Ihr anderes Problem ab. – krdluzni

+0

@krdluzni: NUR, wenn Sie garantieren können, dass eine Kategorie und Projektkombination einzigartig ist. –

0

Eine andere Lösung ist natürlich, so etwas wie „DISTINCT Projekt SELECT FROM ... AS WHERE 'Apple IN (SELECT Kategorie FROM ... AS b WHERE a.Project = b.Project) UND 'Banana' IN (SELECT Kategorie FROM ... AS b WHERE a.Project = b.Project) ", aber das wird relativ schnell ziemlich rechenintensiv. Ich habe auf etwas Eleganteres gehofft und ihr habt nicht enttäuscht.Ich schließe diesen hier größtenteils der Vollständigkeit halber ein, falls jemand anderes diese Frage anspricht. Es ist eindeutig Null Punkte wert. :)

Verwandte Themen