您可以使用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'方法在一两种情况下通常效果更好,但是复杂的查询可能很难维护并且可能会混淆优化器。 –