繁体   English   中英

SQL一对多LEFT OUTER JOIN:执行AND查询

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM