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