1

我正在开发一个在线图书馆的搜索引擎,但我有点卡在这里。搜索标签时,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 提供的答案也值得研究,特别是如果您的目标是优化搜索查询。

4

3 回答 3

2

FROM 子句中以逗号分隔的表列表的功能类似于内部连接,因此您的查询将选择 tagmaps 表和 tags 表中具有相同标签 ID 的所有行,以及这些行中的所有行具有相同图书 ID 的 books 表和 tagmaps 表。然后,HAVING 子句要求从该结果集中返回具有相同书籍 ID 的两行。任何给定的书 ID 在书表中只能有一行(假设书 ID 是书表的主键),因此永远不会满足此条件。

你想要的是一个没有书桌的连接。您正在寻找在 OR 子句的结果中出现两次的相同图书 ID(我相信),因此您不想将这些结果加入 books 表,因为这将确保您永远不会在结果不止一次。

编辑:从概念上讲,你本质上是在结合两种不同的东西。您正在寻找同一本书的标签和标签映射,并且您还从每本书中获取书籍信息。因此,您实际上是在为 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
于 2012-05-15T18:00:42.647 回答
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
(
   ...
)
于 2012-05-15T18:07:02.350 回答
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 个最新的源,它们的标签为 2 和 3,并且没有标签为 1。

于 2018-12-30T13:47:39.170 回答