您可以使用OUTER APPLY:
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
起初,我們得到了所有ItemID
的與計算TagsID
的。然後用Items
表的連接與過濾只有那些誰擁有TagsCount = 2
編輯#1
添加一個樣本:
;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
輸出:
ItemID ItemName ItemCost
1 Item1 100
2 Item2 50
編輯#2
如果要過濾沒有tag3
標記的項目,可以添加左連接。
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
如果喲想通過一些標記過濾您可以使用臨時表項有一個標籤,並讓您不需要標籤,然後加入他們的行列。動態SQL也可能是一個選項。
這非常整齊,我認爲這個小組相當好(相對於外部應用解決方案)。 假設,如果我得到30個條件,你是否仍然使用這種方法30總和(case ...)語句或其他東西? – Vok
@Vok。 。 。絕對。無論條件數量多少,「GROUP BY」基本上都具有恆定的性能(大的開銷是聚合,而不是另一個聚合列的計算)。 'JOIN'方法在一兩種情況下通常效果更好,但是複雜的查詢可能很難維護並且可能會混淆優化器。 –