MySQL select 分隔数据

MySQL select delimited data

我继承了一个 table,其中包含有关某些人群的信息,其中一个字段包含分隔数据,结果与另一个匹配 table。

id_group     Name
-----------------------
1            2|4|5
2            3|4|6
3            1|2

在另一个 table 中,我有一个可能属于一个或多个组的人员列表

id_names     Names
-----------------------
1            Jack
2            Joe
3            Fred
4            Mary
5            Bill

我想对组数据执行 select,这会导致单个字段包含逗号或 space 分隔的名称列表,例如上方第一组行中的这个 "Joe Fred Bill"

我看过使用函数来拆分定界字符串,也看过子查询,但是连接子查询的结果很快就会变得很大。

谢谢!

正如上面 Strawberry 的评论所暗示的那样,有一种方法可以做到这一点,但它太丑陋了。这就像使用胶带完成昂贵的厨房改造。你应该对这样设计数据库的人感到反感。

SELECT g.id_group, GROUP_CONCAT(n.Names SEPARATOR ' ') AS Names
FROM groups AS g JOIN names AS n
  ON FIND_IN_SET(n.id_names, REPLACE(g.Name, '|', ','))
GROUP BY g.id_group;

输出,在 MySQL 5.6:

上测试
+----------+---------------+
| id_group | Names         |
+----------+---------------+
|        1 | Joe Mary Bill |
|        2 | Fred Mary     |
|        3 | Jack Joe      |
+----------+---------------+

此查询的复杂性,以及它将被迫执行 table 扫描且无法优化的事实,应该让您相信 what is wrong with storing a list of id's in a delimited string

更好的解决方案是创建第三个 table,在其中将组中的每个单独成员单独存储在一行中。也就是说,每组多行。

CREATE TABLE group_name (
  id_group INT NOT NULL,
  id_name INT NOT NULL,
  PRIMARY KEY (id_group, id_name)
);

然后你可以用更简单的方式查询,并且你有机会创建索引使查询非常快。

SELECT id_group, GROUP_CONCAT(names SEPARATOR ' ') AS names
FROM groups
JOIN group_name USING (id_group)
JOIN names USING (id_name)

影子是正确的。您的主要问题是数据库中关系的糟糕设计。通常,人们将此类业务问题设计为所谓的 M:N 关系(M 到 N)。为此,您需要 3 tables:

  • 第一个 table 是 groups,它有一个带有主键的 GroupId 字段和一个可读的 name 字段(例如 'group1' 或其他)

  • second table 是 people,看起来与上面显示的完全一样。 (不要忘记在此处的 PeopleId 字段中包含主键)

  • 第三个 table 是一座桥 table,名为 GroupMemberships。那一个有 2 个字段 GroupIdPeopleId。此 table 将前两个相互连接并标记 M:N 关系。一个组可以有 1 到 N 个成员,一个人可以是 1 到 M 个组的成员。

最后,将 select 中的 table 连接在一起并聚合:

SELECT 
    g.Name,
    GROUP_CONCAT(p.Name ORDER BY p.PeopleId DESC SEPARATOR ';') AS Members
FROM
    Groups AS g
    INNER JOIN GroupMemberships AS gm ON g.GroupId = gm.GroupId
    INNER JOIN people AS p ON gm.PeopleId = p.PeopleId
GROUP BY g.Name;