题
我正在为网站实施标签系统。每个对象有多个标签,每个标签有多个对象。这是通过维护一个表来实现的,每个记录有两个值,一个用于对象的 ID 和标签。
我正在编写一个查询来查找与给定标签集匹配的对象。假设我有以下数据(采用 [object] -> [tags]* 格式)
apple -> fruit red food
banana -> fruit yellow food
cheese -> yellow food
firetruck -> vehicle red
如果我想匹配(红色),我应该得到苹果和消防车。如果我想匹配(水果,食物),我应该得到(苹果,香蕉)。
如何编写 SQL 查询来做我想要的事情?
@杰里米·鲁滕,
感谢您的回答。使用的符号用于提供一些示例数据 - 我的数据库确实有一个表,每个记录有 1 个对象 id 和 1 个标签。
其次,我的问题是我需要获取与所有标签匹配的所有对象。将 OR 替换为 AND,如下所示:
SELECT object WHERE tag = 'fruit' AND tag = 'food';
运行时不会产生任何结果。
解决方案
鉴于:
- 对象表(主键id)
- objecttags 表(外键 objectId、tagid)
标签表(主键id)
SELECT distinct o.* from object o join objecttags ot on o.Id = ot.objectid join tags t on ot.tagid = t.id where t.Name = 'fruit' or t.name = 'food';
这似乎是倒退的,因为你想要 and ,但问题是,2 个标签不在同一行,因此, and 不会产生任何结果,因为 1 个单行不能同时是水果和食物。此查询通常会产生重复项,因为每个标签将获得每个对象的 1 行。
如果你真的想这样做,在这种情况下,你将需要一个 group by
, ,和一个 having count = <number of ors>
例如在您的查询中。
SELECT distinct o.name, count(*) as count
from object o join objecttags ot on o.Id = ot.objectid
join tags t on ot.tagid = t.id
where t.Name = 'fruit' or t.name = 'food'
group by o.name
having count = 2;
其他提示
天哪,我可能误解了你原来的评论。
在SQL中执行此操作的最简单方法是使用三个表:
1) Tags ( tag_id, name )
2) Objects (whatever that is)
3) Object_Tag( tag_id, object_id )
然后,您可以快速,轻松,高效地询问您想要的数据问题(只要您正确索引)。如果你想获得想象力,你也可以允许多字标签(有一种优雅的方式,而且我可以想到一种不那么优雅的方式)。
我认为这就是你所拥有的,所以下面的SQL将起作用:
文字方式:
SELECT obj
FROM object
WHERE EXISTS( SELECT *
FROM tags
WHERE tag = 'fruit'
AND oid = object_id )
AND EXISTS( SELECT *
FROM tags
WHERE tag = 'Apple'
AND oid = object_id )
还有其他方法可以做到,例如:
SELECT oid
FROM tags
WHERE tag = 'Apple'
INTERSECT
SELECT oid
FROM tags
WHERE tag = 'Fruit'
@Kyle:您的查询应该更像:
SELECT object WHERE tag IN ('fruit', 'food');
您的查询正在寻找标签既是水果又是食物的行,由于该字段只能有一个值,而不是同时具有一个值,因此无法看到。
将史蒂夫M.的建议与杰里米的结合起来,你将获得你所寻找的单一记录:
select object
from tblTags
where tag = @firstMatch
and (
@secondMatch is null
or
(object in (select object from tblTags where tag = @secondMatch)
)
现在,它不能很好地扩展,但它会得到你想要的东西。我认为有一个更好的方法可以做到这一点,这样你可以很容易地拥有N个匹配的项目而不会对代码产生很大的影响,但它目前让我感到厌烦。
我推荐以下架构。
Objects: objectID, objectName
Tags: tagID, tagName
ObjectTag: objectID,tagID
使用以下查询。
select distinct
objectName
from
ObjectTab ot
join object o
on o.objectID = ot.objectID
join tabs t
on t.tagID = ot.tagID
where
tagName in ('red','fruit')
我建议你的桌子每条记录有1个标签,如下所示:
apple -> fruit
apple -> red
apple -> food
banana -> fruit
banana -> yellow
banana -> food
然后你可以
SELECT object WHERE tag = 'fruit' OR tag = 'food';
如果你真的想按照自己的方式去做,你可以这样做:
SELECT object WHERE tag LIKE 'red' OR tag LIKE '% red' OR tag LIKE 'red %' OR tag LIKE '% red %';