简体   繁体   English

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

[英]SQL one-to-many LEFT OUTER JOIN: perform an AND query

This should be common enough and I'm looking for the "best" way to perform this in one SQL query (MySQL). 这应该足够普遍,我正在寻找一种“最佳”方法来在一个SQL查询(MySQL)中执行此操作。

I have three tables, an items table, a linker table and a tags table. 我有三个表,一个items表,一个linker表和一个tags表。 Items can be tagged multiple times, so the linker is a simple foreign key linker table: 项可以被多次标记,因此链接器是一个简单的外键链接器表:

items   | linker  | tags  
--------+---------+-------
item_id | item_id | tag_id
...     | tag_id  | name  
--------+---------+-------

I can search items for single tags easily, how would I go to search items that have 2 or more specific tags? 我可以搜索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"

How does a sane person perform search for 2 or more tags, an item must have ALL the tags, ie an AND query? 一个理智的人如何执行2个或更多标签的搜索,一个项目必须具有所有标签,即AND查询?


Edit: What I have so far is the following, which works and doesn't seem to be slow, but looks crazy: 编辑:到目前为止,我有以下内容,它可以工作并且似乎并不慢,但是看起来很疯狂:

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"
)

Assuming the PK for the linker table is (item_id,tag_id), I would use the following: 假设链接器表的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
  )
;

The above query should be easy to maintain. 上面的查询应该易于维护。 You can easily add or subtract required tag names. 您可以轻松添加或减去必需的标签名称。 You just need to make sure the having count matches the number of names in the list. 您只需要确保计数与列表中的名称数匹配即可。

If the linker table PK is not (item_id,tag_id), then the having clause would have to change to having count(distinct tag_id)=3 , though that query may not perform so well, depending on how many duplicate (item_id,tag_id) pairs exist. 如果链接器表PK不是(item_id,tag_id),那么haveing子句将不得不更改为having count(distinct tag_id)=3 ,尽管该查询的效果可能不佳,具体取决于有多少重复(item_id,tag_id)对存在。

Another nice feature about the above is you can easily answer questions like, which items are associated with at least 2 of the following list of tags ('tag1','tag2','tag3'). 关于上述内容的另一个不错的功能是,您可以轻松回答以下问题,例如哪些项目与以下标签列表中的至少2个相关联('tag1','tag2','tag3')。 You just need to set the having count to the correct value. 您只需要将计数计数设置为正确的值即可。

If I understand correctly (which I'm not sure I do :) ... ), you want to find results that contain a certain string (like a regular expression search). 如果我理解正确(不确定(我不确定:) ...),则您想查找包含某个字符串的结果(例如正则表达式搜索)。

you could try the RLIKE function 您可以尝试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")

I think this is what you mean, but maybe not: 我认为这是您的意思,但也许不是:

http://dev.mysql.com/doc/refman/5.0/en/regexp.html http://dev.mysql.com/doc/refman/5.0/en/regexp.html


Or if each entry has only one tag per entry, what about using IN : 或者,如果每个条目只有一个标签,那么使用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 http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_in


And why not just a basic OR 为什么不只是基本的OR

 WHERE `tags`.`name` = "tag-a" OR `tags`.`name` = "tag-b"

I hope I'm understanding your goal correctly, please let me know if I don't. 希望我能正确理解您的目标,如果不正确,请告诉我。

edit I mis-read a part of your question...I may not be sane, but hope this doesn't disqualify me :P 编辑我误读了部分问题...我可能并不理智,但希望这不会使我失去资格:P

To restate your question, you want all columns from table items that have all the tags in some list, is that correct? 要重述您的问题,您希望表items中的所有列都具有某个列表中的所有tags ,对吗? If so, I think you need to join to your tags table for each and use an INNER JOIN instead of a LEFT OUTER JOIN . 如果是这样,我认为您需要为每个表加入tags表,并使用INNER JOIN而不是LEFT OUTER JOIN Something like this: 像这样:

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"

Using an INNER JOIN will select only rows that have all three tags. 使用INNER JOIN将仅选择具有所有三个标签的行。 I'm not sure how you would do this with a variable number of tags (which I think is what you really want). 我不确定如何使用可变数量的标签(我认为这是您真正想要的)来执行此操作。

Of course this has been asked already: How to filter SQL results in a has-many-through relation 当然,这已经被问到了: 如何通过has-man-through关系过滤SQL结果

Turns out my interim solution is one of the fastest (number 4 in the linked question), here it is: 原来我的临时解决方案是最快的解决方案之一(链接的问题中排名第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