MySQL搜索包含所有标签的项目

时间:2012-05-15 17:34:16

标签: mysql tagging

我正在为一个在线图书馆的搜索引擎工作,但我有点被困在这里。搜索标签时,OR搜索(即带有“tag1”或“tag2”的书籍)工作正常,但AND搜索给我带来了一些麻烦。

我用于此的表(及其列)是:

books  | book_id, other_info
tagmap | map_id, book_id, tag_id
tags   | tag_id, tag_text

由于用户可以启用/禁用一堆其他搜索选项,因此查询由PHP生成。搜索带有“tag1”和“tag2”标签的图书时,会生成以下查询:

SELECT DISTINCT b.book_id, b.other_info
FROM books b, tagmap tm, tags t
WHERE b.book_id = "NA"
OR ( (t.tag_text IN ("tag1", "tag2"))
      AND tm.tag_id = t.tag_id
      AND b.book_id = tm.book_id )
HAVING COUNT(tm.book_id)=2

WHERE行(不提供任何结果)是存在的,因此可以更容易地将其他参数串联到查询中。我知道这可以处理得更好,但是现在这没关系。

当进行OR搜索(相同的查询但没有HAVING COUNT行)时,它返回数据库中具有这些标记之一的两本书,但是当在数据库中搜索具有BOTH标记的一本书时,它没有回报。

查询有什么问题?这不是/一种方法吗?我在俯瞰什么?

谢谢!

编辑:根据请求,每个表中与应该返回的书相关的数据:

books table:
book_id     110

tagmap table:
book_id     110    110
tag_id      15     16

tags table:
tag_id      15     16
tag_text    tag1   tag2

解决方案:我所要做的就是包括

GROUP BY b.book_id
在HAVING COUNT行之前

。就那么简单。 taz提供的答案也值得研究,特别是如果您的目标是优化搜索查询。

3 个答案:

答案 0 :(得分:1)

FROM子句中以逗号分隔的表的作用类似于内部联接,因此您的查询是选择tagmaps表中的所有行以及具有相同标记ID的tags表,以及这些行的所有行books表和tagmaps表中具有相同书ID的行。然后,HAVING子句要求从具有相同书ID的结果集返回两行。书籍表中只能有一行带有任何给定的书籍ID(假设书籍ID是书籍表的主键),因此这种情况永远不会得到满足。

你想要的是没有书籍表的连接。您正在寻找在OR条款结果中出现两次相同的图书ID(我相信),因此您不希望将这些结果加入图书表格,因为这样可以确保您永远不会拥有相同的图书ID在结果中不止一次。

编辑:从概念上讲,你实际上是在结合两种不同的东西。您正在为同一本书寻找标签和标签,您还可以从每本书中获取书籍信息。因此,您实际上是为tagmaps表中的同一本书ID的每个实例提取重复的other_info数据,然后使用distinct子句将该重复数据减少到一行,因为您只需要书籍ID和other_info。我会考虑使用两个查询或子查询来执行此操作。也可能有其他[更好]的方式。我不得不玩弄它来解决它。

对于初学者,请尝试

SELECT DISTINCT tm.book_id, b.other_info
FROM tagmap tm inner join tags t
    on tm.tag_id = t.tag_id
  left join books b
    on tm.book_id = b.book_id
HAVING count(tm.book_id) = 2

答案 1 :(得分:1)

SELECT book_id FROM tagmap JOIN tags ON (tag_id) WHERE tag_text = "tag1"
INTERSECT
SELECT book_id FROM tagmap JOIN tags ON (tag_id) WHERE tag_text = "tag2"

将整个事物包装为子查询,以选择您需要的其他书籍信息:

SELECT book_id, other_info FROM books WHERE book_id IN
(
   ...
)

答案 2 :(得分:0)

好吧,看起来我为mysql数据库做了复杂的解决方案(对于其他任何解决方案都应该适用)。因此,数据库结构是这样的:

source (id)
tag(id)
tags(source, tag)

涵盖此要求:

  • 获取具有任何选定标签或具有所有选定标签的源
  • 获取不包含排除标记的源

在此处查询

 SELECT source.id 
 FROM source
 -- optional join, if you need to include sources with tags:
 LEFT JOIN tags tags_include ON source.id = tags_include.source
 -- optional join, if you need to exclude sources with tags
 LEFT JOIN tags tags_exclude ON source.id = tags_exclude.source
      -- here list of excluded tags
      and tags_exclude.tag in(1)
 WHERE
    -- optional condition, tags which you need to include
    tags_include.tag IN (2, 3)
    -- optional condition, which will exlcude sources with excluded tags
    and tags_exclude.source is null
 GROUP BY source.id
 -- optional having, in case when you include tags with "AND" strategy
 -- count should be equal to count of selected tags
 -- if you fetch sources with "OR" strategy, ignore this having
 HAVING count(1) >= 2
 ORDER BY source.id DESC
 LIMIT 0, 50;

因此,此查询将获取50个最新的源,这些源的ID为2和3,标记为1。