[英]SQL one-to-many LEFT OUTER JOIN: perform an AND query
这应该足够普遍,我正在寻找一种“最佳”方法来在一个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"
)
假设链接器表的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),那么haveing子句将不得不更改为having count(distinct tag_id)=3
,尽管该查询的效果可能不佳,具体取决于有多少重复(item_id,tag_id)对存在。
关于上述内容的另一个不错的功能是,您可以轻松回答以下问题,例如哪些项目与以下标签列表中的至少2个相关联('tag1','tag2','tag3')。 您只需要将计数计数设置为正确的值即可。
如果我理解正确(不确定(我不确定:) ...),则您想查找包含某个字符串的结果(例如正则表达式搜索)。
您可以尝试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
要重述您的问题,您希望表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
将仅选择具有所有三个标签的行。 我不确定如何使用可变数量的标签(我认为这是您真正想要的)来执行此操作。
当然,这已经被问到了: 如何通过has-man-through关系过滤SQL结果
原来我的临时解决方案是最快的解决方案之一(链接的问题中排名第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"
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.