Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Abfrage nach einem Satz in einer relationalen Datenbank

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.