开发者

SQL - Returning Each Record Only Once

Here's my tables:

tblBusiness
BusinessID, BusinessName

tblTags
TagID, Tag

tblBusinessTagLink
BusinessID, TagID

Any business can have multiple tags applied to it. Now lets say a user is filtering down so that they find only businesses that are tagged 'Office Supplies' and 'Technology'

What SQL statement should I use? Is there a开发者_如何学JAVA better design for my tables than what I've presented here?


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 

This selects all businesses that are in either one of the categories. To select only those in both categories, you could append a

HAVING COUNT(*) = 2

The method you are using (three tables to represent a m:n relationship) is the standard way to solve this task, you can keep that.

Personally, I would not use "hungarian notation" for table names (i.e. no "tbl") and I would not use plural table names (i.e. not "Tags"), especially when the other tables are not plural either.


Answering the first comment below:

For larger data sets, the performance of this query relies on indexes. All the primary keys need an index, naturally. In tblBusinessTagLink you should have a composite index covering both fields and one additional index for the field that does not come first in the composite index.

The WHERE keywords LIKE '%technology%' idea is a bad one, mostly because for any LIKE conditions other than start-of-field searches an index cannot be used (i.e. performance will degrade rapidly as your data set grows) and partly because it should be WHERE ','+keywords+',' LIKE '%,technology,%' to begin with or you will get partial matches/false positives.

Also, it might be a bit more efficient to query by TagId. This way you can remove one table from the JOIN entirely:

FROM 
  tblBusiness AS b 
  INNER JOIN tblBusinessTagLink AS l ON l.BusinessId = b.BusinessId 
WHERE 
  l.TagId IN (1, 2)

If you intend to query by TagName however, an index on this field will be absolutely necessary as well.


You can use simple JOIN to get record

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'


You can use the INTERSECT set operation to merge the 2 queries (one for 'Office Supplies' and one for 'Technology').

However if you are using MySQL (which does not support INTERSECT), you can use a UNION ALL with a 'HAVING COUNT(*) = 2' like this.


EDIT:

You can also use the second option without the UNION ALL like so:

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;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜