2

这应该很常见,我正在寻找在一个 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"
)
4

4 回答 4

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')。您只需将拥有计数设置为正确的值。

于 2012-12-30T04:31:41.087 回答
0

要重申您的问题,您希望表items中的所有列都包含tags某个列表中的所有列,对吗?如果是这样,我认为您需要为每个人加入您的tags表格并使用 aINNER JOIN而不是 a 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"

使用 anINNER JOIN将仅选择具有所有三个标签的行。我不确定您将如何使用可变数量的标签来做到这一点(我认为这是您真正想要的)。

于 2012-12-30T01:06:57.090 回答
0

当然这已经被问过了:How to filter SQL results in a has-many-through relationship

原来我的临时解决方案是最快的解决方案之一(链接问题中的第 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"
)
于 2012-12-30T01:20:25.570 回答
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

于 2012-12-30T00:00:36.187 回答