Ich werde nicht kommentieren, ob es dafür ein besser geeignetes Schema gibt (es ist durchaus möglich), aber für ein Schema mit Spalten name
und item
, sollte die folgende Abfrage funktionieren. (Mysql-Syntax)
SELECT k.name
FROM (SELECT DISTINCT name FROM sets) AS k
INNER JOIN sets i1 ON (k.name = i1.name AND i1.item = 1)
INNER JOIN sets i2 ON (k.name = i2.name AND i2.item = 3)
INNER JOIN sets i3 ON (k.name = i3.name AND i3.item = 5)
LEFT JOIN sets ix ON (k.name = ix.name AND ix.item NOT IN (1, 3, 5))
WHERE ix.name IS NULL;
Die Idee ist, dass wir alle gesetzten Schlüssel in k
haben , die wir dann mit den Setitemdaten zu sets
zusammenfügen einmal für jedes Set-Element in dem Set, nach dem wir suchen, in diesem Fall drei. Jeder der drei Inner Joins mit Tabellenaliasen i1
, i2
und i3
alle Satznamen herausfiltern, die das mit dieser Verbindung gesuchte Element nicht enthalten. Schließlich haben wir einen linken Join mit sets
mit Tabellenalias ix
, das alle zusätzlichen Gegenstände im Set enthält, dh jeden Gegenstand, nach dem wir nicht gesucht haben. ix.name
ist NULL
für den Fall, dass keine zusätzlichen Elemente gefunden werden, was genau das ist, was wir wollen, also das WHERE
Klausel. Die Abfrage gibt eine Zeile zurück, die den Satzschlüssel enthält, wenn der Satz gefunden wird, andernfalls keine Zeilen.
Bearbeiten: Die Idee hinter Collapsars Antwort scheint viel besser zu sein als meine, also hier ist eine etwas kürzere Version davon mit Erklärung.
SELECT sets.name
FROM sets
LEFT JOIN (
SELECT DISTINCT name
FROM sets
WHERE item NOT IN (1, 3, 5)
) s1
ON (sets.name = s1.name)
WHERE s1.name IS NULL
GROUP BY sets.name
HAVING COUNT(sets.item) = 3;
Die Idee dabei ist, dass die Unterabfrage s1
wählt die Schlüssel aller Sätze aus, die andere Elemente als die gesuchten enthalten. Wenn wir also verließen, verbinden Sie sets
mit s1
, s1.name
ist NULL
wenn das Set nur Artikel enthält, nach denen wir suchen. Wir gruppieren dann nach Satzschlüssel und filtern alle Sätze mit der falschen Anzahl von Elementen heraus. Uns bleiben dann nur Sets, die nur gesuchte Artikel enthalten und die richtige Länge haben. Da Sets einen Gegenstand nur einmal enthalten können, kann es nur ein Set geben, das diese Kriterien erfüllt, und das ist das, wonach wir suchen.
Bearbeiten: Mir ist gerade eingefallen, wie ich das ohne den Ausschluss machen kann.
SELECT totals.name
FROM (
SELECT name, COUNT(*) count
FROM sets
GROUP BY name
) totals
INNER JOIN (
SELECT name, COUNT(*) count
FROM sets
WHERE item IN (1, 3, 5)
GROUP BY name
) matches
ON (totals.name = matches.name)
WHERE totals.count = 3 AND matches.count = 3;
Die erste Unterabfrage ermittelt die Gesamtzahl der Elemente in jedem Satz und die zweite ermittelt die Anzahl der übereinstimmenden Elemente in jedem Satz. Wenn matches.count
3 ist, enthält das Set alle Artikel, nach denen wir suchen, und wenn totals.count
ist auch 3, das Set enthält keine zusätzlichen Elemente.