如何使用 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,108, 9, 10 不同。在后者中,8 会被 FIND_IN_SET 找到,但 9(缺少开头的 space)不会。
  • 表现不佳。