如何使用 SQL normalize/flatten 将属性设置为交叉点 table?
How to normalize/flatten a set attribute to an intersection table using SQL?
我接到了改进旧数据库的任务。我可以使用连接到 MariaDB 的 phpMyAdmin。
有一个 table 区域有一个名为 Super 的列。 Super 属性存储 m:m 与称为 Subcontinents 的 table 的关系。这里有一个问题:Super 不是使用交集 table,而是类型 Set/Enum 并且包含与给定区域关联的 all 次大陆。允许的值在 table 定义中进行了硬编码,并且 未 与外键链接。
区域定义
Name
Type
Id
int(11)
Name
text
Super
set('1', '2', ...) = Ids of Subcontinents table
区域示例
Id
Name
Super
123
Atlas
17
456
Europe
8,9,10,11
次大陆定义
Name
Type
Id
int(11)
Subcontinent
text
次大陆示例
Id
Subcontinent
8
Northern Europe
9
Eastern Europe
10
Southern Europe
11
Western Europe
17
Eastern Africa
我现在要做的是在两个 table 之间创建一个交集 table。我找不到如何用多个 Super 值压平记录。所需的输出将与此类似:
RegionId
SubcontinentId
123
17
456
8
456
9
456
10
456
11
我试图查询像 SELECT id, super, (SELECT * FROM super) as target FROM Region
这样的 Super 属性,但显然这是无效的语法。我还尝试将设置值映射到一个整数,但我现在也不知道如何从那里开始。在互联网上搜索了很多关于规范化数据库的 material,遗憾的是 none 其中包含一个带有集合的示例。
PS:我知道如何创建 tables,在它们之间移动数据并添加约束。
显然有一个函数 FIND_IN_SET
可以帮助解决这个问题。语法是:
FIND_IN_SET(pattern, strlist)
可以将其用作联接中的条件:
SELECT r.id, r.super, s.id
FROM Region as r
JOIN Subcontinents as s
ON FIND_IN_SET(s.id, r.super)
这导致:
r.id
r.super
s.id
456
8,9,10,11
8
456
8,9,10,11
9
456
8,9,10,11
10
456
8,9,10,11
11
请注意@Akina在评论中指出的陷阱:
- 没有套装,它们实际上是 CSV 超值包。
FIND_IN_SET
是一个字符串函数。
- 空格很重要。
8,9,10
与 8, 9, 10
不同。在后者中,8
会被 FIND_IN_SET
找到,但 9
(缺少开头的 space)不会。
- 表现不佳。
我接到了改进旧数据库的任务。我可以使用连接到 MariaDB 的 phpMyAdmin。
有一个 table 区域有一个名为 Super 的列。 Super 属性存储 m:m 与称为 Subcontinents 的 table 的关系。这里有一个问题:Super 不是使用交集 table,而是类型 Set/Enum 并且包含与给定区域关联的 all 次大陆。允许的值在 table 定义中进行了硬编码,并且 未 与外键链接。
区域定义
Name | Type |
---|---|
Id | int(11) |
Name | text |
Super | set('1', '2', ...) = Ids of Subcontinents table |
区域示例
Id | Name | Super |
---|---|---|
123 | Atlas | 17 |
456 | Europe | 8,9,10,11 |
次大陆定义
Name | Type |
---|---|
Id | int(11) |
Subcontinent | text |
次大陆示例
Id | Subcontinent |
---|---|
8 | Northern Europe |
9 | Eastern Europe |
10 | Southern Europe |
11 | Western Europe |
17 | Eastern Africa |
我现在要做的是在两个 table 之间创建一个交集 table。我找不到如何用多个 Super 值压平记录。所需的输出将与此类似:
RegionId | SubcontinentId |
---|---|
123 | 17 |
456 | 8 |
456 | 9 |
456 | 10 |
456 | 11 |
我试图查询像 SELECT id, super, (SELECT * FROM super) as target FROM Region
这样的 Super 属性,但显然这是无效的语法。我还尝试将设置值映射到一个整数,但我现在也不知道如何从那里开始。在互联网上搜索了很多关于规范化数据库的 material,遗憾的是 none 其中包含一个带有集合的示例。
PS:我知道如何创建 tables,在它们之间移动数据并添加约束。
显然有一个函数 FIND_IN_SET
可以帮助解决这个问题。语法是:
FIND_IN_SET(pattern, strlist)
可以将其用作联接中的条件:
SELECT r.id, r.super, s.id
FROM Region as r
JOIN Subcontinents as s
ON FIND_IN_SET(s.id, r.super)
这导致:
r.id | r.super | s.id |
---|---|---|
456 | 8,9,10,11 | 8 |
456 | 8,9,10,11 | 9 |
456 | 8,9,10,11 | 10 |
456 | 8,9,10,11 | 11 |
请注意@Akina在评论中指出的陷阱:
- 没有套装,它们实际上是 CSV 超值包。
FIND_IN_SET
是一个字符串函数。- 空格很重要。
8,9,10
与8, 9, 10
不同。在后者中,8
会被FIND_IN_SET
找到,但9
(缺少开头的 space)不会。 - 表现不佳。