我有一个SQL数据库,我在其中存储与用户关联的用户和标签(多对多关系)。我有users
表,tags
表和"桥接"的经典模式。表格usertag
将用户与标签相关联:
users table:
+---------+---------+
| Id | Name |
+---------+---------+
| 1 | Alice |
| 2 | Bob |
| 3 | Carl |
| 4 | David |
| 5 | Eve |
+---------+---------+
tags table:
+---------+---------+
| Id | Name |
+---------+---------+
| 10 | Red |
| 20 | Green |
| 30 | Blue |
+---------+---------+
usertag table:
+---------+---------+
| UserId | TagId |
+---------+---------+
| 2 | 10 |
| 2 | 20 |
| 1 | 30 |
| 4 | 20 |
| 4 | 10 |
| 4 | 30 |
| 5 | 10 |
+---------+---------+
现在,我使用GROUP_CONCAT()
函数进行查询以将所有用户及其标记检索为逗号分隔字段:
SELECT u.*, GROUP_CONCAT(ut.tagid) as tags FROM users as u LEFT JOIN usertag as ut ON u.id = ut.userid GROUP BY u.id
给了我正确的输出:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 1 | Alice | 30 |
| 2 | Bob | 10,20 |
| 3 | Carl | (null) |
| 4 | David | 10,30,20 |
| 5 | Eve | 10 |
+---------+---------+----------+
问题在于,现在我想在其上实现标记过滤,即能够通过标记(或多个标记)查询用户。过滤器应该使用AND运算符。
例如:获取标签为Red(10)AND Green(20)的用户:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 2 | Bob | 10,20 |
| 4 | David | 10,30,20 |
+---------+---------+----------+
另一个例子:获取标签为Red(10)的用户:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 2 | Bob | 10,20 |
| 4 | David | 10,30,20 |
| 5 | Eve | 10 |
+---------+---------+----------+
另一个例子:获取标签为Red(10),Green(20)和Blue(30)的用户:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 4 | David | 10,30,20 |
+---------+---------+----------+
我该如何实现这样的查询?关于SO的This question非常相似,它实际上有效,但它并不处理GROUP_CONCAT()
字段,这是我想保留的字段
这里是SQL小提琴http://sqlfiddle.com/#!9/291a5c/8
修改
可以想象这个查询有效:
检索标记为Red(10)和Blue(20)的所有用户:
SELECT u.name, GROUP_CONCAT(ut.tagid)
FROM users as u
JOIN usertag as ut ON u.id = ut.userid
WHERE ut.tagid IN (10,20)
GROUP BY u.id
HAVING COUNT(DISTINCT ut.tagid) = 2
给出了:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 2 | Bob | 10,20 |
| 4 | David | 10,20 |
+---------+---------+----------+
用户名是正确的(Bob和David),但Tags
字段缺少David列表中的标记30!
答案 0 :(得分:1)
left join
tags
表,并在join子句中包含要搜索的ID,并检查having
中的计数。
SELECT u.id,u.name,GROUP_CONCAT(ut.tagid) as tags
FROM users u
LEFT JOIN usertag as ut ON u.id = ut.userid
LEFT JOIN tags t ON t.id=ut.tagid AND t.ID IN (10,20,30) --change this as needed
GROUP BY u.id,u.name
HAVING COUNT(ut.tagid) >= COUNT(t.id) AND COUNT(t.id) = 3 --change this number to the number of tags
如果值有限,可以使用FIND_IN_SET
。例如,
SELECT * FROM (
SELECT u.*, GROUP_CONCAT(ut.tagid) as tags
FROM users as u
LEFT JOIN usertag as ut ON u.id = ut.userid
GROUP BY u.id
) T
WHERE FIND_IN_SET('10',tags) > 0 AND FIND_IN_SET('20',tags) > 0