【发布时间】:2011-01-01 05:34:54
【问题描述】:
这是我的桌子:
tblBusiness
BusinessID, BusinessName
tblTags
TagID, Tag
tblBusinessTagLink
BusinessID, TagID
任何企业都可以应用多个标签。现在假设用户正在过滤,以便他们只找到标记为“办公用品”和“技术”的企业
我应该使用什么 SQL 语句?有没有比我在这里展示的更适合我的桌子的设计?
【问题讨论】:
这是我的桌子:
tblBusiness
BusinessID, BusinessName
tblTags
TagID, Tag
tblBusinessTagLink
BusinessID, TagID
任何企业都可以应用多个标签。现在假设用户正在过滤,以便他们只找到标记为“办公用品”和“技术”的企业
我应该使用什么 SQL 语句?有没有比我在这里展示的更适合我的桌子的设计?
【问题讨论】:
SELECT
b.BusinessId,
b.BusinessName
FROM
tblBusiness AS b
INNER JOIN tblBusinessTagLink AS l ON l.BusinessId = b.BusinessId
INNER JOIN tblTags AS t ON t.TagId = l.TagId
WHERE
t.TagName IN ('Technology', 'Office Supplies')
GROUP BY
b.BusinessId,
b.BusinessName
这会选择属于任一类别的所有企业。要仅选择这两个类别中的那些,您可以附加一个
HAVING COUNT(*) = 2
您使用的方法(三个表来表示 m:n 关系)是解决此任务的标准方法,您可以保留它。
就个人而言,我不会对表名使用“匈牙利表示法”(即没有“tbl”),也不会使用复数表名(即不是“Tags”),尤其是当其他表也不是复数时。
回答下面的第一条评论:
对于较大的数据集,此查询的性能依赖于索引。自然,所有主键都需要一个索引。在tblBusinessTagLink 中,您应该有一个涵盖这两个字段的复合索引和一个附加索引,用于在复合索引中没有出现在第一位的字段。
WHERE keywords LIKE '%technology%' 的想法很糟糕,主要是因为对于除起始字段搜索之外的任何 LIKE 条件,都无法使用索引(即,随着数据集的增长,性能会迅速下降),部分原因是它应该是以WHERE ','+keywords+',' LIKE '%,technology,%' 开头,否则您将得到部分匹配/误报。
另外,通过TagId 查询可能会更高效一些。这样您就可以完全从 JOIN 中删除一个表:
FROM
tblBusiness AS b
INNER JOIN tblBusinessTagLink AS l ON l.BusinessId = b.BusinessId
WHERE
l.TagId IN (1, 2)
但是,如果您打算通过TagName 查询,则该字段上的索引也是绝对必要的。
【讨论】:
你可以使用简单的JOIN来获取记录
SELECT t.Tag, b.BusinessName
FROM tblBusiness b, tblTags t, tblBusinessTagLink l
WHERE t.TagID = l.TagID
AND l.BusinessID = b.BusinessID
AND t.Tag = 'Office Supplies'
【讨论】:
您可以使用INTERSECT set 操作来合并 2 个查询(一个用于“办公用品”,一个用于“技术”)。
但是,如果您使用的是 MySQL(不支持 INTERSECT),则可以使用带有 'HAVING COUNT(*) = 2' like this 的 UNION ALL。
编辑:
您也可以像这样使用第二个选项而不使用 UNION ALL:
select Name from tblBusiness
left join tblBusinessTagLink on tblBusinessTagLink.BusinessID = tblBusiness.ID
left join tblTags on tblTags.TagID = tblBusinessTagLink.TagID
where Tag = 'Office Supplies' or Tag = 'Technology'
group by name
having count(Name) = 2;
【讨论】: