2017-06-05 91 views
0

我有一个SQL数据库,其中存储了与用户关联的用户和标记(多对多关系)。我有经典模式与users表,tags表和“桥梁”表usertag哪个环节有标签的用户:SQL标记列表和标记过滤

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运算符。

例如:获取与标签红用户(10)和绿色(20):

output: 
    +---------+---------+----------+ 
    | Id  | Name | Tags  | 
    +---------+---------+----------+ 
    | 2  | Bob  | 10,20 | 
    | 4  | David | 10,30,20 | 
    +---------+---------+----------+ 

又如:获取与标签红用户(10):

output: 
    +---------+---------+----------+ 
    | Id  | Name | Tags  | 
    +---------+---------+----------+ 
    | 2  | Bob  | 10,20 | 
    | 4  | David | 10,30,20 | 
    | 5  | Eve  | 10  | 
    +---------+---------+----------+ 

又如:获取与标签红色(10),绿(20)蓝(30)用户:

output: 
    +---------+---------+----------+ 
    | Id  | Name | Tags  | 
    +---------+---------+----------+ 
    | 4  | David | 10,30,20 | 
    +---------+---------+----------+ 

我如何能实现这样的查询? This question上等都非常相似,它的实际工作,但它不与GROUP_CONCAT()领域这是我想保持,因为它是

这里的SQL小提琴处理http://sqlfiddle.com/#!9/291a5c/8

编辑

有人可能会想象,这个查询工作:

获取与标签红(10)和蓝色的所有用户(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和大卫),但Tags场从大卫的列表中缺少的标签30!

+0

要检查的标签的最大数量是多少? –

+0

@vkp是16,不再有 –

回答

1

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 

如果有限制的值,另一个选项是使用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 
+0

啊!好。这是否可以在SQLite上运行?如果我根本不需要标签名称,为什么需要引入“标签”表? –

+0

这是因为你需要来自该表的计数来进行比较。 –

+0

了解。你会推荐任何其他模式来简化查询吗?这是可以在sqlite上工作的东西吗? –