我有一个 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 |
+---------+---------+
Run Code Online (Sandbox Code Playgroud)
现在,我使用以下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
Run Code Online (Sandbox Code Playgroud)
这给了我正确的输出:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 1 | Alice | 30 |
| 2 | Bob | 10,20 |
| 3 | Carl | (null) |
| 4 | David | 10,30,20 |
| 5 | Eve | 10 |
+---------+---------+----------+
Run Code Online (Sandbox Code Playgroud)
问题是现在我想在此基础上实现标签过滤,即能够通过标签(或多个标签)查询用户。过滤器应该使用 AND 运算符工作。
例如:获取带有标签 Red (10) AND Green (20) 的用户:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 2 | Bob | 10,20 |
| 4 | David | 10,30,20 |
+---------+---------+----------+
Run Code Online (Sandbox Code Playgroud)
另一个示例:获取带有标签 Red (10) 的用户:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 2 | Bob | 10,20 |
| 4 | David | 10,30,20 |
| 5 | Eve | 10 |
+---------+---------+----------+
Run Code Online (Sandbox Code Playgroud)
另一个示例:获取带有标签 Red (10)、Green (20) 和 Blue (30) 的用户:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 4 | David | 10,30,20 |
+---------+---------+----------+
Run Code Online (Sandbox Code Playgroud)
我怎样才能实现这样的查询?这个关于 SO 的问题非常相似,它实际上有效,但它不涉及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
Run Code Online (Sandbox Code Playgroud)
这使:
output:
+---------+---------+----------+
| Id | Name | Tags |
+---------+---------+----------+
| 2 | Bob | 10,20 |
| 4 | David | 10,20 |
+---------+---------+----------+
Run Code Online (Sandbox Code Playgroud)
哪个用户名是正确的(Bob 和 David),但该Tags字段缺少 David 列表中的标签 30!
left join该tags表包括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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7010 次 |
| 最近记录: |