2009-11-05 57 views
5

我有用户和标签之间的m:n关系。一个用户可以有m个标签,一个标签可以属于n个用户。表是这个样子:SQL SELECT与m:n的关系

USER: 
ID 
USER_NAME 

USER_HAS_TAG: 
USER_ID 
TAG_ID 

TAG: 
ID 
TAG_NAME 

比方说,我需要选择的所有用户,谁都有标签“苹果”,“橙色”和“香蕉”。使用SQL(MySQL DB)完成此操作的最有效方法是什么?

回答

4

除了其他很好的答案,它也可以检查条件的WHERE子句:

select * 
from user u 
where 3 = (
    select count(distinct t.id) 
    from user_has_tag uht 
    inner join tag t on t.id = uht.tag_id 
    where t.name in ('apple', 'orange', 'banana') 
    and uht.user_id = u.userid 
) 

count(distinct ...)确保标签只计算一次,即使用户有多个“香蕉”标签。

顺便说一句,该网站fruitoverflow.com尚未注册:)

8
SELECT u.* 
FROM (
     SELECT user_id 
     FROM tag t 
     JOIN user_has_tag uht 
     ON  uht.tag_id = t.id 
     WHERE tag_name IN ('apple', 'orange', 'banana') 
     GROUP BY 
       user_id 
     HAVING COUNT(*) = 3 
     ) q 
JOIN user u 
ON  u.id = q.user_id 

通过消除HAVING COUNT(*),你OR而不是AND(虽然它不会是最有效的方法)

通过与2更换3,你有精确定义二三标签的用户。

通过将= 3替换为>= 2,您将获得定义了三个标签中至少两个的用户。

+0

那是肯定没有最有效的,这将汇总所有记录。例如。如果没有用户符合条件,将会做很多无用的工作 3 selfjoin是有效的方式 – noonex 2009-11-05 15:50:00

+0

'@ noonex':对于真实世界的数据(大量用户,大量标签,高用户标记基数),这是一种高效的办法。 'tag_name IN(...)'是一个可优化的条件,它只会聚合带有mathing标签的记录。如果你需要使查询匹配'4'或'20'标签呢?使用自连接时,您需要重写查询结构,只用参数“GROUP BY”。 – Quassnoi 2009-11-05 15:59:18

0
SELECT * 
FROM USER u 
INNER JOIN USER_HAS_TAG uht 
ON u.id = uht.user_id 
INNER JOIN TAG t 
ON uht.TAG_ID = t.ID 
WHERE t.TAG_NAME IN ('apple','orange','banana') 
+0

这不起作用 – tputkonen 2009-11-05 14:36:27

+0

如果你想要标签为'apple','orange'或'banana'的用户,但不是全部三个, – MarthyM 2016-06-29 09:06:11

3

你可以加入做到这一切......

select u.* 
from user u 

inner join user_has_tag ut1 on u.id = ut1.user_id 
inner join tag t1 on ut1.tag_id = t1.id and t1.tag_name = 'apple' 

inner join user_has_tag ut2 on u.id = ut2.user_id 
inner join tag t2 on ut2.tag_id = t2.id and t2.tag_name = 'orange' 

inner join user_has_tag ut3 on u.id = ut3.user_id 
inner join tag t3 on ut3.tag_id = t3.id and t3.tag_name = 'banana' 
+0

从技术上讲,更高效的方式将使用适当的tag_id和self_oin只有user_has_tag表(3次)。但方法是正确的 – noonex 2009-11-05 15:53:32