2016-08-15 2 views
2

Ich habe drei einfache Tabellen:Wie Abfragen von Daten basierend auf mehreren 'Tags' in SQL?

Artikel

ItemID (int, PK) 
ItemName (nvarchar50) 
ItemCost (int) 

Stichworte

TagID (int, PK) 
TagName (nvarchar50) 

ItemTags

ItemID (int, FK->Items) 
TagID (int, FK->Tags) 

Wie kann ich eine Anfrage an die Wirkung von ‚alle Artikel finden schreiben mit tag1 und tag2 getaggt, aber nicht tag3 '?

Muss ich ein komplett anderes Schema verwenden?

Antwort

5

Ich mag, dies zu tun mit GROUP BY und HAVING:

SELECT it.ItemId 
FROM ItemTags it JOIN 
    Tags t 
    ON t.TagId = it.TagId 
GROUP BY it.ItemId 
HAVING SUM(CASE WHEN t.TagName = 'Tag1' THEN 1 ELSE 0 END) > 0 AND 
     SUM(CASE WHEN t.TagName = 'Tag2' THEN 1 ELSE 0 END) > 0 AND 
     SUM(CASE WHEN t.TagName = 'Tag3' THEN 1 ELSE 0 END) = 0; 

Jede Bedingung in der HAVING Klausel prüft, ob einen Tag. Die ersten beiden verwenden > 0, um zu sagen, dass das Tag für das Element existieren muss. Die dritte verwendet = 0, um zu sagen, dass das Tag für das Element nicht existieren darf.

+0

Das ist ziemlich ordentlich, und ich stelle mir die Gruppe nach Skalen relativ gut vor (gegenüber der äußeren Anwen- dungslösung). Hypothetisch, würde ich, wenn ich 30 Bedingungen auslöste, diesen Ansatz mit 30 Summensätzen (Fall ...) oder etwas anderem verwenden? – Vok

+0

@Vok. . . Absolut. Die "GROUP BY" hat im Wesentlichen konstante Leistung unabhängig von der Anzahl der Bedingungen (der große Aufwand ist die Aggregation, nicht die Berechnung einer anderen aggregierten Spalte). Die 'JOIN'-Methode funktioniert oft besser für eine oder zwei Bedingungen, aber die komplexen Abfragen können schwer zu warten sein und den Optimierer verwirren. –

1

Sie OUTER können GILT:

SELECT i.* 
FROM Items i 
OUTER APPLY (
    SELECT COUNT(*) as TagsCount 
    FROM ItemTags it 
    INNER JOIN Tags t 
     ON t.TagID = it.TagID 
    WHERE i.ItemID = it.ItemID 
     AND t.TagName IN ('tag1','tag2') 
) as tt 
WHERE TagsCount = 2 

Zuerst bekommen wir alle ItemID 's mit gezählt TagsID' s. Dann kommen Sie mit Items Tabellen mit Filtern nur diejenigen, die TagsCount = 2

EDIT # 1

hinzufügen Probe haben:

;WITH Items AS (
    SELECT * 
    FROM (VALUES 
    (1,'Item1',100),(2,'Item2',50),(3,'Item3',90),(4,'Item4',63),(5,'Item5',75) 
    )as t(ItemID,ItemName,ItemCost) 
) 
, Tags AS (
    SELECT * 
    FROM (VALUES 
    (1,'tag1'),(2,'tag2'),(3,'tag3'),(4,'tag4'),(5,'tag5') 
    ) as t(TagID, TagName) 
) 
, ItemTags AS (
    SELECT * 
    FROM (VALUES 
    (1,1),(1,2),   --This 
    (2,1),(2,2),(2,3),  --and that records we need to get 
    (3,1),  (3,3),(3,4), 
      (4,2),   (4,5), 
    (5,1) 
    ) as t(ItemID, TagID) 
) 

SELECT i.* 
FROM Items i 
CROSS APPLY (
    SELECT COUNT(*) as TagsCount 
    FROM ItemTags it 
    INNER JOIN Tags t 
     ON t.TagID = it.TagID 
    WHERE i.ItemID = it.ItemID 
     AND t.TagName IN ('tag1','tag2') 
    HAVING COUNT(*) = 2 
) as tt 

Ausgang:

ItemID ItemName ItemCost 
1  Item1  100 
2  Item2  50 

EDIT # 2

Wenn Sie Elemente ohne tag3-Tags filtern möchten, können Sie den linken Join hinzufügen.

SELECT i.* 
FROM Items i 
CROSS APPLY (
    SELECT COUNT(*) as TagsCount 
    FROM ItemTags it 
    INNER JOIN Tags t 
     ON t.TagID = it.TagID 
    WHERE i.ItemID = it.ItemID 
     AND t.TagName IN ('tag1','tag2') 
    HAVING COUNT(*) = 2 
) as tin 
LEFT JOIN (
    SELECT it.Itemid 
    FROM ItemTags it 
    INNER JOIN Tags t 
     ON t.TagID = it.TagID 
    WHERE t.TagName IN ('tag3') 
    ) tnot 
    ON tnot.Itemid = i.itemid 
WHERE tnot.ItemId is NULL 

Wenn yo von einigen Tags filtern möchten Sie temporäre Tabelle mit Elementen verwenden können, die eine Tags hat, und hat Tags, die Sie nicht brauchen, und dann kommen. Auch dynamisches SQL kann eine Option sein.

+0

Vielen Dank für diesen Vorschlag - in der Lage zu sein, IN zu verwenden, ist großartig, um die Liste der Bedingungen zu erweitern. Ich habe versucht, AND t.TagName NICHT IN ('tag3') hinzuzufügen, aber das schien nicht zu funktionieren. – Vok

+0

Fügen Sie einige Änderungen hinzu, vielleicht würde Ihnen das helfen. – gofr1

Verwandte Themen