这应该很常见,我正在寻找在一个SQL查询(MySQL)中执行此操作的“最佳”方法。
我有三个表,一个items
表,一个linker
表和一个tags
表。项可以多次标记,因此链接器是一个简单的外键链接器表:
items | linker | tags
--------+---------+-------
item_id | item_id | tag_id
... | tag_id | name
--------+---------+-------
我可以轻松搜索items
单个标签,如何搜索包含2个或更多特定标签的商品?
SELECT *, `tags`.`name`
FROM `items`
LEFT OUTER JOIN `linker` USING (`item_id`)
LEFT OUTER JOIN `tags` USING (`tag_id`)
WHERE `tags`.`name` = "tag-a"
理智的人如何搜索2个或更多标签,项目必须包含所有标签,即AND
个查询?
编辑:到目前为止我所拥有的是以下内容,它起作用并且看起来并不慢,但看起来很疯狂:
SELECT `items`.* FROM `items`
LEFT OUTER JOIN `linker` USING (`item_id`)
LEFT OUTER JOIN `tags` USING (`tag_id`)
WHERE (
`item_id` IN (SELECT item_id FROM linker LEFT JOIN tags USING (tag_id) WHERE name = "tag-a")
AND `item_id` IN (SELECT item_id FROM linker LEFT JOIN tags USING (tag_id) WHERE name = "tag-b")
AND `item_id` IN (SELECT item_id FROM linker LEFT JOIN tags USING (tag_id) WHERE name = "tag-c")
AND `item_stuff` = "whatever"
)
答案 0 :(得分:1)
假设链接器表的PK是(item_id,tag_id),我会使用以下内容:
select *
from items
where item_id in (
select item_id
from linker
join tags using(tag_id)
where name in ('tag1', 'tag2', 'tag3')
group by item_id
having count(tag_id)=3
)
;
上述查询应该易于维护。您可以轻松添加或减去所需的标签名称。您只需要确保计数与列表中的名称数相匹配。
如果链接器表PK不是(item_id,tag_id),那么having子句必须更改为having count(distinct tag_id)=3
,尽管该查询可能执行得不好,具体取决于重复次数(item_id,tag_id)对存在。
关于上述内容的另一个不错的功能是,您可以轻松回答以下问题:哪些项目与以下标签列表中的至少2个相关联('tag1','tag2','tag3')。您只需要将计数设置为正确的值。
答案 1 :(得分:0)
如果我理解正确(我不确定:) ...),你想找到包含某个字符串的结果(比如正则表达式搜索)。
您可以尝试RLIKE
功能
SELECT *, `tags`.`name`
FROM `items`
LEFT OUTER JOIN `linker` USING (`item_id`)
LEFT OUTER JOIN `tags` USING (`tag_id`)
WHERE `tags`.`name` RLIKE("tag-a"|"tag-b")
我认为这就是你的意思,但也许不是:
http://dev.mysql.com/doc/refman/5.0/en/regexp.html
或者,如果每个条目每个条目只有一个标记,那么使用IN
:
SELECT *, `tags`.`name`
FROM `items`
LEFT OUTER JOIN `linker` USING (`item_id`)
LEFT OUTER JOIN `tags` USING (`tag_id`)
WHERE `tags`.`name` IN ("tag-a","tag-b")
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_in
为什么不只是一个基本的OR
WHERE `tags`.`name` = "tag-a" OR `tags`.`name` = "tag-b"
我希望我能正确理解你的目标,如果我没有,请告诉我。
编辑我误读了你问题的一部分......我可能不理智,但希望这不会让我失去资格:P
答案 2 :(得分:0)
要重述您的问题,您希望表items
中包含某个列表中所有tags
的所有列都正确吗?如果是这样,我认为您需要加入tags
表,并使用INNER JOIN
代替LEFT OUTER JOIN
。像这样:
SELECT DISTINCT `items`.*
FROM `items` a
JOIN `linker` b
ON b.item_id=a.item_id
JOIN `tags` c1
ON c1.tag_id=b.tag_id
and c1.name = "tag-a"
JOIN `tags` c2
ON c2.tag_id=b.tag_id
and c2.name = "tag-a"
JOIN `tags` c3
ON c3.tag_id=b.tag_id
and c3.name = "tag-c"
使用INNER JOIN
将仅选择包含所有三个标记的行。我不确定如何使用可变数量的标签(我认为这是你真正想要的)这样做。
答案 3 :(得分:0)
当然已经问过这个问题:How to filter SQL results in a has-many-through relation
原来我的临时解决方案是最快的(链接问题中的第4位),这里是:
SELECT *
FROM `items`
WHERE (
`item_id` IN (SELECT item_id FROM linker INNER JOIN tags USING (tag_id) WHERE name = "tag-a")
AND `item_id` IN (SELECT item_id FROM linker INNER JOIN tags USING (tag_id) WHERE name = "tag-b")
AND `item_id` IN (SELECT item_id FROM linker INNER JOIN tags USING (tag_id) WHERE name = "tag-c")
AND `item_stuff` = "whatever"
)