SQL many-to-many-Matching
-
09-06-2019 - |
Frage
Ich bin ein Tagging-System für eine Website zu implementieren. Es gibt mehrere Tags pro Objekt und mehr Objekte pro Tag. Dies wird erreicht, indem Sie eine Tabelle mit zwei Werten pro Datensatz beibehalten wird, eine für die Iden des Objekts und dem Tag.
Ich suche eine Abfrage zu schreiben, um die Objekte zu finden, die einen bestimmten Satz von Tags entsprechen. Angenommen, ich hatte die folgenden Daten (in [object] -> [tags] * Format)
apple -> fruit red food
banana -> fruit yellow food
cheese -> yellow food
firetruck -> vehicle red
Wenn ich (rot) übereinstimmen soll, sollte ich Apfel und Feuerwehrauto bekommen. Wenn ich will (Obst, Lebensmittel) entsprechen soll ich (Apfel, Banane).
Wie kann ich eine SQL-Abfrage schreiben Sie das tun, was ich will?
@ Jeremy Ruten,
Vielen Dank für Ihre Antwort. Die Notation verwendet wurde verwendet, um einige Beispieldaten zu geben -. Meine Datenbank eine Tabelle mit 1 Objekt-ID und 1 Tag pro Datensatz hat
Zweitens, mein Problem ist, dass ich alle Objekte erhalten müssen, die alle Tags entsprechen. Setzen Sie dabei OR für eine UND etwa so:
SELECT object WHERE tag = 'fruit' AND tag = 'food';
Die Renditen keine Ergebnisse bei der Ausführung.
Lösung
Gegeben:
- Objekttabelle (Primärschlüssel id)
- objecttags Tabelle (Fremdschlüssel objectId, MarkierungslD)
-
-Tags Tabelle (Primärschlüssel id)
SELECT distinct o.* from object o join objecttags ot on o.Id = ot.objectid join tags t on ot.tagid = t.id where t.Name = 'fruit' or t.name = 'food';
Dies scheint nach hinten, da Sie wollen und, aber das Problem ist, 2-Tags sind nicht in der gleichen Zeile, und daher ein und liefert nichts, da 1 einreihig nicht sowohl eine Frucht und ein Lebensmittel sein kann. Diese Abfrage wird Duplikate in der Regel ergeben, da Sie 1 Zeile jedes Objekt erhalten wird, pro Tag.
Wenn Sie wollen wirklich eine tun und in diesem Fall erhalten Sie eine group by
benötigen, und eine having count = <number of ors>
in Ihrer Abfrage zum Beispiel.
SELECT distinct o.name, count(*) as count
from object o join objecttags ot on o.Id = ot.objectid
join tags t on ot.tagid = t.id
where t.Name = 'fruit' or t.name = 'food'
group by o.name
having count = 2;
Andere Tipps
Oh Gott kann ich Ihre ursprünglichen Kommentar-mis interpretiert.
Der einfachste Weg, dies in SQL zu tun wäre, um drei Tabellen zu haben:
1) Tags ( tag_id, name )
2) Objects (whatever that is)
3) Object_Tag( tag_id, object_id )
Dann können Sie praktisch jede Frage stellen Sie die Daten wollen einfach schnell und effizient (Sie Index in angemessener Weise zur Verfügung gestellt). Wenn Sie Lust bekommen möchten, können Sie Multi-Wort-Tags können auch (es gibt eine elegante Art und Weise, und eine weniger elegante Art und Weise, kann ich mir vorstellen).
Ich gehe davon aus, dass das, was du hast, so dass diese SQL wird im folgenden arbeiten:
Die wörtliche Weise:
SELECT obj
FROM object
WHERE EXISTS( SELECT *
FROM tags
WHERE tag = 'fruit'
AND oid = object_id )
AND EXISTS( SELECT *
FROM tags
WHERE tag = 'Apple'
AND oid = object_id )
Es gibt auch andere Möglichkeiten, wie Sie es tun können, wie zum Beispiel:
SELECT oid
FROM tags
WHERE tag = 'Apple'
INTERSECT
SELECT oid
FROM tags
WHERE tag = 'Fruit'
@ Kyle: Ihre Abfrage sollte wie:
SELECT object WHERE tag IN ('fruit', 'food');
Ihre Anfrage wurde für die Zeilen, wo der Tag sowohl Obst und das Essen war, das als das Feld nicht möglich ist, zu sehen, nur einen Wert haben kann, nicht beide gleichzeitig.
Kombinieren Steve M. Vorschlag mit Jeremys Sie einen einzelnen Datensatz bekommen mit, was Sie suchen:
select object
from tblTags
where tag = @firstMatch
and (
@secondMatch is null
or
(object in (select object from tblTags where tag = @secondMatch)
)
Nun, das nicht sehr gut skalieren, aber es wird bekommen, was Sie suchen. Ich denke, dass es einen besseren Weg, dies zu gehen zu tun, so können Sie leicht N Anzahl passender Artikel ohne viel Einfluss auf den Code haben, aber es entgeht mir zur Zeit.
Ich empfehle das folgende Schema.
Objects: objectID, objectName
Tags: tagID, tagName
ObjectTag: objectID,tagID
Mit der folgenden Abfrage.
select distinct
objectName
from
ObjectTab ot
join object o
on o.objectID = ot.objectID
join tabs t
on t.tagID = ot.tagID
where
tagName in ('red','fruit')
Ich würde Tisch haben 1 Tag pro Datensatz schlagen vor, wie folgt aus:
apple -> fruit
apple -> red
apple -> food
banana -> fruit
banana -> yellow
banana -> food
Dann konnte man nur
SELECT object WHERE tag = 'fruit' OR tag = 'food';
Wenn Sie es wirklich Ihre Art und Weise zu tun, wollen aber Sie es wie folgt tun könnte:
SELECT object WHERE tag LIKE 'red' OR tag LIKE '% red' OR tag LIKE 'red %' OR tag LIKE '% red %';