如何根据多个标签查询数据'在SQL中?

时间:2016-08-15 12:18:13

标签: sql sql-server schema

我有三个简单的表格:

产品

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

标签

TagID (int, PK)
TagName (nvarchar50)

ItemTags

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

如何编写查询以查找标记为tag1和tag2的所有项目,而不是tag3'?

我是否需要使用完全不同的架构?

3 个答案:

答案 0 :(得分:5)

我喜欢使用GROUP BYHAVING

执行此操作
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;

HAVING子句中的每个条件都会检查一个标记。前两个使用> 0来表示该项必须存在标记。第三个使用= 0来表示该项目的标签不得存在。

答案 1 :(得分:1)

您可以使用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的{​​{1}}个。{然后加入TagsID表,只过滤那些Items

的人

修改#1

添加样本:

TagsCount = 2

输出:

;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

修改#2

如果您要过滤没有ItemID ItemName ItemCost 1 Item1 100 2 Item2 50 标记的项目,可以添加左连接。

tag3

如果你想过滤某些标签你可以使用临时表和包含一个标签的项目,并且有你不需要的标签然后加入它们。动态SQL也可以是一种选择。

答案 2 :(得分:0)

这里有一个更简单的解决方案

let filteredTags = Set<Tag.ID>()
CREATE TABLE sketch (
  id int
)

CREATE TABLE tag (
  id int
)

CREATE TABLE sketchTag (
  sketchId int,
  tagId int
)
SELECT *
FROM sketch
INNER JOIN (
    SELECT sketchId
    FROM sketchTag
    WHERE tagId IN \(filteredTags)
    GROUP BY sketchId
    HAVING COUNT(tagId) = \(filteredTags.count)
) AS sketchTag
ON sketch.id = sketchTag.sketchId