MySQL将两个表与另一个表链接并运行过滤的查询

时间:2018-07-18 11:13:51

标签: mysql

对不起,如果已经发布了,我看了一下,但真的不知道要搜索什么!

问题

我目前正在建立一个系统,用各种标签“标记”一个学生社团。我们的想法是,我们可以创建并应用所需的任何标签,然后使用API​​使用这些标签为不同的流程授权不同的社会(即检查是否已应用特定的标签)。

我们还使用这些标签来创建学生组。我希望用户能够设置标签过滤器(例如,他们应该能够说“我想组成一个由理学院的所有学术团体组成的小组”,在这些小组中,两个“学术”标签和“科学”标签。

我们的桌子的设计概述如下:

Societies Table:
+------------+-------------------+
| Society ID |   Society Name    |
+------------+-------------------+
|          1 | Physics Society   |
|          2 | Chemistry Society |
+------------+-------------------+

Tags Table (Where the tags are defined):
+--------+--------------+
| Tag ID |   Tag Name   |
+--------+--------------+
|      1 | Academic     |
|      2 | Science      |
|      3 | Volunteering |
+--------+--------------+

Linking Table:
+---------+--------+----------+
| Link ID | Tag ID | Group ID |
+---------+--------+----------+
|       1 |      1 |        1 |
|       2 |      2 |        1 |
|       3 |      1 |        2 |
|       4 |      2 |        2 |
|       5 |      3 |        2 |
+---------+--------+----------+

在这种情况下,物理学会已被标记为“学术”和“科学”标签。

尝试解决方案

我现在需要一种搜索​​特定社会的方法。为了确保可以创建任何组,我认为我们需要包括运算符AND,OR和NOT以及方括号,然后从中构造一个SQL查询吗?如果有更好的方法,请纠正我!

用户将按以下格式输入过滤器(不一定是这种情况,但我想不出其他任何方法!):

({1,3} OR {6}) NOT {5}

使用PHP,然后将其转换为SQL查询:

WHERE (tag_id IN (1,3) OR tag_id IN (6)) AND tag_id NOT IN (5)

给出完整的查询

SELECT tag_links.society_id FROM tag_links WHERE (tag_id IN (1,3) OR tag_id IN (6)) AND tag_id NOT IN (5)

不幸的是,这行不通-结果多次返回相同的社会ID。我认为这是因为,由于每个社团有多行,因此查询不会排除任何带有tag_id 5标记的组。它不会返回将社团与tag_id 5链接的行。

有更好的方法吗?除了基本知识之外,我还没有丰富的SQL查询经验,因此可能遗漏了一些明显的东西...

非常感谢您的帮助!

托比

1 个答案:

答案 0 :(得分:0)

我认为您想要一种找到具有特定标记集的社会的方法。如果是这样,那么这是应该起作用的常规查询:

SELECT
    s.ID, s.Name
FROM Societies s
INNER JOIN Linking lnk
    ON s.ID = lnk.Soc_ID
INNER JOIN Tags t
    ON lnk.Tag_ID = t.ID
WHERE
    t.Name IN ('Academic', 'Science')
GROUP BY
    S.ID
HAVING COUNT(DISTINCT t.ID) = 2;

上面的查询将找到所有同时具有“学术”和“科学”标签的社会。根据您的样本数据,这只会找到物理学会。

要在下面发表您的评论,如果您要查找具有两个标签的社会,而又具有一个或多个标签的社会,那么查询会变得有些丑陋,因为我们需要条件聚合:< / p>

SELECT
    s.ID, s.Name
FROM Societies s
INNER JOIN Linking lnk
    ON s.ID = lnk.Soc_ID
INNER JOIN Tags t
    ON lnk.Tag_ID = t.ID
GROUP BY
    S.ID
HAVING
    SUM(CASE WHEN t.Name = 'Academic'     THEN 1 ELSE 0 END) > 0 AND
    SUM(CASE WHEN t.Name = 'Science'      THEN 1 ELSE 0 END) > 0 AND
    SUM(CASE WHEN t.Name = 'Volunteering' THEN 1 ELSE 0 END) = 0;