MySql 多对多搜索

MySql many to many search

我在处理多对多查找时遇到了一些问题。我有以下表格:

mysql> desc tags;
+------------+------------------+------+-----+---------------------+----------------+
| Field      | Type             | Null | Key | Default             | Extra              |
+------------+------------------+------+-----+---------------------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL                | auto_increment |
| created_at | timestamp        | NO   |     | 0000-00-00 00:00:00 |                   |
| updated_at | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| user_id    | bigint(20)       | NO   |     | NULL                |                |
| name       | varchar(64)      | NO   |     | NULL                |                |
+------------+------------------+------+-----+---------------------+----------------+

mysql> desc response_and_tag_relationships;
+-------------+------------------+------+-----+---------+-------+
| Field       | Type             | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+-------+
| response_id | int(10) unsigned | NO   | PRI | NULL    |       |
| tag_id      | int(10) unsigned | NO   | PRI | NULL    |       |
+-------------+------------------+------+-----+---------+-------+

mysql> desc survey_responses;
+--------------+------------------+------+-----+---------------------+----------------+
| Field        | Type             | Null | Key | Default             | Extra          |
+--------------+------------------+------+-----+---------------------+----------------+
| id           | int(10) unsigned | NO   | PRI | NULL                | auto_increment |
| created_at   | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| updated_at   | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| survey_id    | bigint(20)       | NO   |     | NULL                |                |
| response     | text             | NO   |     | NULL                |                |
| score        | smallint(6)      | NO   |     | NULL                |                |
| recipient_id | bigint(20)       | NO   |     | NULL                |                |
+--------------+------------------+------+-----+---------------------+----------------+

mysql> desc surveys;
+--------------+------------------+------+-----+---------------------+----------------+
| Field        | Type             | Null | Key | Default             | Extra          |
+--------------+------------------+------+-----+---------------------+----------------+
| id           | int(10) unsigned | NO   | PRI | NULL                | auto_increment |
| created_at   | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| updated_at   | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| survey_token | varchar(255)     | NO   |     | NULL                |                |
| type         | smallint(6)      | NO   |     | NULL                |                |
| name         | varchar(255)     | NO   |     | NULL                |                |
+--------------+------------------+------+-----+---------------------+----------------+

mysql> desc people;
+------------+------------------+------+-----+---------------------+----------------+
| Field      | Type             | Null | Key | Default             | Extra          |
+------------+------------------+------+-----+---------------------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL                | auto_increment |
| created_at | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| updated_at | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| email      | varchar(255)     | NO   |     | NULL                |                |
| last_sent  | datetime         | NO   |     | NULL                |                |
| name       | varchar(255)     | NO   |     | NULL                |                |
| dob        | date             | NO   |     | NULL                |                |
+------------+------------------+------+-----+---------------------+----------------+

我需要对响应中的标签进行分组。所以我想出了以下 SQL(以我有限的知识),它似乎可以完成这项工作:

SELECT 
    rat.tag_id, 
    rat.response_id,
    t.name,
    sr.response,
    p.name,
    p.email
FROM 
    response_and_tag_relationships rat 
INNER JOIN tags t ON t.id=rat.tag_id 
INNER JOIN survey_responses sr ON sr.id=rat.response_id
INNER JOIN surveys s ON s.id = sr.survey_id
INNER JOIN people p ON p.id=sr.recipient_id
WHERE  
    t.name IN (SELECT name FROM tags);

并产生以下结果:

+--------+-------------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------+-----------------------------------+
| tag_id | response_id | name        | response                                                                                                                                                                                                                             | name                      | email                             |
+--------+-------------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------+-----------------------------------+
|      1 |           1 | ex          | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |
|      3 |           1 | repudiandae | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |
|      4 |           1 | nam         | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |
|      5 |           1 | excepturi   | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |
|      6 |           1 | quasi       | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |
|      7 |           1 | perferendis | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |
|      8 |           1 | nisi        | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |
|      9 |           1 | sint        | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |

但我认为我的逻辑倒退了,因为这是响应驱动的,而不是标签驱动的(我认为...)。我基本上只需要在响应调用 api 方法中将结果与 return 相匹配的标签列表 - 那么有没有更好的方法来做到这一点?

编辑

我理想的结果是(虽然我不相信这是可能的):

+--------+-------------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------+-----------------------------------+
| tag_id | response_id | name        | response                                                                                                                                                                                                                             | name                      | email                             |
+--------+-------------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------+-----------------------------------+
|      1,3,4,5,6,7,8,9 |           1 | ex          | Repudiandae nam excepturi quasi perferendis nisi sint. Et excepturi id facere modi et sed. Eius nihil repellat veritatis voluptas.                                                                                                   | Miss Sally Breitenberg    | kkiehn@breitenberg.net            |

我还需要在 Laravel eloquent orm 中构建它,但我猜最好只使用原始查询

好的,从预期的结果来看,它更像是您在尝试对标签 ID 进行分组,是的,您可以使用 group_concat 函数作为

SELECT 
    group_concat(rat.tag_id) as tag_ids, 
    rat.response_id,
    t.name,
    sr.response,
    p.name,
    p.email
FROM 
    response_and_tag_relationships rat 
INNER JOIN tags t ON t.id=rat.tag_id 
INNER JOIN survey_responses sr ON sr.id=rat.response_id
INNER JOIN surveys s ON s.id = sr.survey_id
INNER JOIN people p ON p.id=sr.recipient_id
group by rat.response_id

现在上面针对每个不同 response_id 的查询会将 tag_id 分组为逗号分隔的字符串。

另请注意,当使用 group by 和 select 从多到多的数据时,可能 return 任何随机值,例如,如果我们将 t.name 保留在 selection 那么它可能是连接后的多对多关系中的任何一个,对于其他值也是如此。

理想情况下,您永远不应该 select 在这些情况下使用这些列或在 group_concat 之类的聚合函数中使用它们。理想的查询是

SELECT 
        group_concat(rat.tag_id) as tag_ids, 
        rat.response_id
    FROM 
        response_and_tag_relationships rat 
    INNER JOIN tags t ON t.id=rat.tag_id 
    INNER JOIN survey_responses sr ON sr.id=rat.response_id
    INNER JOIN surveys s ON s.id = sr.survey_id
    INNER JOIN people p ON p.id=sr.recipient_id
    group by rat.response_id

如果您还需要 select 中的其他列,那么如前所述,它可能是连接数据中的任何随机值。如果您想要全部,则对每个人使用 group_concat()

查看 group_concat 的手册,因为对于大型数据集,您可能需要增加 group_concat_max_len