SQL 标签列表和标签过滤

Gia*_*ini 5 mysql sql tags

我有一个 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!

Vam*_*ala 7

left jointags表包括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)