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 个字段 GroupId
和 PeopleId
。此 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;
我继承了一个 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 个字段GroupId
和PeopleId
。此 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;