动态内容类型:一个 table 有很多列,还是一个 table 每个?
Dynamic content types: One table with many columns or one table for each?
我 运行 不时进入这个问题,我想知道最终最好的解决方案是什么。
示例:
这里以"dynamic forms"为例。您有一个表单编辑器,可以添加表单字段(输入)。每个表单字段转换为不同类型的数据:
- A
<textarea>
成为 TEXT
列。
- 一个
<input type="text">
变成了一个VARCHAR(X)
。
- 一个
<input type="range">
变成了一个FLOAT
。
- A
<select>
可以是逗号分隔的列表 (TEXT
)、每个条目(外键)的单独 table 或对现有 [=71= 的引用](外键)。
存储每个 for 的 结构 的简单方法可能是这样的:
没关系。但真正的问题是:如果有人填写此动态表格,我们如何正确存储数据?
我看到了两个解决方案:
- A) 创建一个 table,其中每个预期数据类型(
VARCHAR
、TEXT
、FLOAT
、...)都有一个 NULLable 列。这可行,但会留下很多丑陋的 NULL 值,因为每个表单字段值只占用一列。此外,如果 <select>
字段引用现有的 table. 字段,则会出现问题
- B) 为每个预期的数据类型创建一个 table。这将使事情比版本 A 更干净,但在存储和获取数据时需要更多的复杂性,因为程序必须知道类型,使用正确的 table,等等
我想知道:是否有更好的/其他解决方案?应该考虑什么是更好的方法?我可以想象这样的事情会经常出现 - 也许还有我不知道的第三种选择?
请记住 "form editor" 示例只是一个 示例 。这个问题适用于许多其他事物:模板编辑器、设置编辑器等。我问的是 "general" 解决这个问题的方法,不是专门针对表单编辑器的;)
注意:我不会考虑使用额外的非关系数据库作为解决方案。我主要是想把数据放在一个地方,所以问题只关注(My)SQL。
can add form fields (inputs).
让我们记住关系模型用于表示 'structured information'。
如果您可以自由添加(某物),听起来您的信息是非结构化的。
这些表单域来自哪里?它们代表业务实体还是实体的属性?那你不是已经在业务的数据模型中有了这些吗? (在非常一般的意义上使用 'business'。)
But the real question is: How do we properly store the data, ...
不,真正的问题是:我们如何正确地 retrieve/query 数据?数据代表什么?商业用户会问什么问题?
如果您还没有为业务建模entities/attributes,那么您将不知道如何甚至是否可以回答用户的问题。
因此,如果用户只是想随机 facts/fields 访问数据库并期望随机询问有关它的问题,则您没有结构化数据,并且关系数据库不是正确的工具工作。这听起来更像是 NoSQL 或某种 document/tagged/XML/JSON 数据库。 (尽管我怀疑 "database" 是否是这些的正确术语。)
I'm asking about a "general" approach with this problem, not specifically for form editors
This thing keeps popping up again and again and everyone I know seems to have a different take on it - but none of them has a really "proper" solution
在关系需求之后,当然是最高阶指令,我将采用通用方法和正确解决方案作为over-arching 指令。
1 关系提升
但首先,更正。为了将数据模型提升到 Relational,没有它 straight-forward 解决方案将非常复杂(这正是 [a] 有这么多不正确方法的原因,并且[b] none 个是正确的解决方案)。
作为“关系”进行推广和营销的大部分“文学”和教科书实际上是 anti-relational。带有关系标签的 1970 年前记录归档系统,这显然是许多作者和“理论家”所能理解的全部。这种 pre-relational 文件系统与 关系模型 的主要区别在于,引用是逻辑的,而不是物理的。
- 而在 pre-relational 系统中,物理 记录通过物理指针关联,例如
ID (INTEGER; GUID; UUID; etc)
,在关系范式中,逻辑 行(不是记录)由关系键关联。
- Relational Key需要由数据组成,是逻辑的,不是物理的(物理指针不是数据)。
记录 ID 在关系数据库中没有用处。他们的目的是传播“理论家”的理解:
- 让你迷惑
- 保持 1970 年前的状态,pre-relational,身体心态
- 从而防止关系思维和数据库
至少,每个这样的 Record ID
将是一个 additional 列和一个 additional 索引。
- Additional,不是,而是,因为逻辑数据(Keys)上的索引是数据完整性所必需的,否则你会有重复且不完整。
因此,您的要求提升为关系型,每列少一列和索引table:
备注
我所有的数据模型都在 IDEF1X 中呈现,这是关系数据库建模的标准。
- IDEF1X Introduction 初学者
- IDEF1X Model Anatomy 有经验的人,正在寻求复习
2 个必需的关系键
第二个需要了解的是关系键的完整性特征,您不会拥有它,因为“理论家”很幸运地没有意识到它。这是与问题相关的解释,而不是完整的教程。假设您确实需要数据完整性和关系完整性( 关系模型 提供的逻辑特征(与引用完整性不同,后者是由 SQL 提供的物理设施)。我们将需要它,以便控制(提供完整性)数据层次结构中所需的数据结构 'lower',用于“存储”。
- 虽然上述[1]对于那个目的是正确的,但我们还有一个额外的要求:我们需要
FieldType
in FormField.PK
这样它就可以迁移到它的 child tables.
- 但是当然,我们一定不能从[1]上面的逻辑
FormField.PK
中减去。如果我们这样做,它将失去其完整性和意义。
- 所以我们:
- 添加所需的关系键 [2]
- 使它成为主键因为那是被迁移的
- 我们通过将其设为 备用键
来维护原始主键 [1]
- 将关系
FieldType::FormField
从 Non-Identifying 更改为 Identifying.
3 关系解决方案 • 子类型
现在解决方案很简单。
I see two solutions to this [A] or [B]
肯定是 [B],通过一些改进使其达到标准。
[A] 是一个没有完整性的非规范化混乱,这将是维护的噩梦。 Null 是非常有问题的,外键中的 Null 是自杀。
but [B] will require a lot more complexity when storing and fetching data, since the program has to know the type, use the right table, etc etc.
存储空间
完全没有。
- 耐心等待,解决方案如下
- 您清楚地了解数据(“存储”)table必须键入(或强制转换)
- 它需要一个普通的Exclusive Subtype结构。 (自 1960 年代以来,我们在 DBMS 中就有了子类型。)
- 我已经在 Basetype 中提供了输入(转换)控件
- 首先,通过在parent
FormField
主键中插入FieldType
,这样它就会被迁移
- 其次,通过使用Non-Identifying关系,使其不在child
UserField
的Key中
- 但它仍然是 non-key 列,并且它可用作 Basetyp 的 Discriminator
- 现在,可以理解为什么 [1][ 和 [2] 必须先实施了
- 看看您是否能欣赏关系键提供的关系完整性(与参照完整性不同):
UserField
是存在于FormName
中的CONSTRAINED
到FieldNames
- 逻辑数据完整性级别,这是关系数据库中的标准票价,在“理论家”以欺诈方式销售的 pre-relational 物理(
Record ID
基础)归档系统中无法实现关系
- Subtype Exposition 初学者
此外,您应该使用 ACID 事务(自第一个版本以来 SQL 合规性的要求),而不是直接从程序中使用 INSERT/UPDATE/DELETE
。这将简化应用程序和程序代码,并消除其他数以千计的错误。如果按照 OLTP 标准实施,它可以最大限度地减少锁定和争用,并减少(如果不能消除)死锁。
检索
不是真的。可能不简单,绝对不复杂,但就是straight-forward.
- 使用
VIEW
(这是VIEWS
的经典用法)
- 用于检索已知子类型:
每个子类型一个视图
- 用于检索未知子类型:
任何子类型的一个视图(带有UNION
)(这将return每个基本类型只有一个,无论它是哪个子类型)
(这不是“超类型”,anti-relational 深受“理论家”喜爱的概念,天哪。)
- 这将使 SQL 编码也 straight-forward。
再来一个
我 运行 不时进入这个问题,我想知道最终最好的解决方案是什么。
示例:
这里以"dynamic forms"为例。您有一个表单编辑器,可以添加表单字段(输入)。每个表单字段转换为不同类型的数据:
- A
<textarea>
成为TEXT
列。 - 一个
<input type="text">
变成了一个VARCHAR(X)
。 - 一个
<input type="range">
变成了一个FLOAT
。 - A
<select>
可以是逗号分隔的列表 (TEXT
)、每个条目(外键)的单独 table 或对现有 [=71= 的引用](外键)。
存储每个 for 的 结构 的简单方法可能是这样的:
没关系。但真正的问题是:如果有人填写此动态表格,我们如何正确存储数据?
我看到了两个解决方案:
- A) 创建一个 table,其中每个预期数据类型(
VARCHAR
、TEXT
、FLOAT
、...)都有一个 NULLable 列。这可行,但会留下很多丑陋的 NULL 值,因为每个表单字段值只占用一列。此外,如果<select>
字段引用现有的 table. 字段,则会出现问题
- B) 为每个预期的数据类型创建一个 table。这将使事情比版本 A 更干净,但在存储和获取数据时需要更多的复杂性,因为程序必须知道类型,使用正确的 table,等等
我想知道:是否有更好的/其他解决方案?应该考虑什么是更好的方法?我可以想象这样的事情会经常出现 - 也许还有我不知道的第三种选择?
请记住 "form editor" 示例只是一个 示例 。这个问题适用于许多其他事物:模板编辑器、设置编辑器等。我问的是 "general" 解决这个问题的方法,不是专门针对表单编辑器的;)
注意:我不会考虑使用额外的非关系数据库作为解决方案。我主要是想把数据放在一个地方,所以问题只关注(My)SQL。
can add form fields (inputs).
让我们记住关系模型用于表示 'structured information'。
如果您可以自由添加(某物),听起来您的信息是非结构化的。
这些表单域来自哪里?它们代表业务实体还是实体的属性?那你不是已经在业务的数据模型中有了这些吗? (在非常一般的意义上使用 'business'。)
But the real question is: How do we properly store the data, ...
不,真正的问题是:我们如何正确地 retrieve/query 数据?数据代表什么?商业用户会问什么问题?
如果您还没有为业务建模entities/attributes,那么您将不知道如何甚至是否可以回答用户的问题。
因此,如果用户只是想随机 facts/fields 访问数据库并期望随机询问有关它的问题,则您没有结构化数据,并且关系数据库不是正确的工具工作。这听起来更像是 NoSQL 或某种 document/tagged/XML/JSON 数据库。 (尽管我怀疑 "database" 是否是这些的正确术语。)
I'm asking about a "general" approach with this problem, not specifically for form editors
This thing keeps popping up again and again and everyone I know seems to have a different take on it - but none of them has a really "proper" solution
在关系需求之后,当然是最高阶指令,我将采用通用方法和正确解决方案作为over-arching 指令。
1 关系提升
但首先,更正。为了将数据模型提升到 Relational,没有它 straight-forward 解决方案将非常复杂(这正是 [a] 有这么多不正确方法的原因,并且[b] none 个是正确的解决方案)。
作为“关系”进行推广和营销的大部分“文学”和教科书实际上是 anti-relational。带有关系标签的 1970 年前记录归档系统,这显然是许多作者和“理论家”所能理解的全部。这种 pre-relational 文件系统与 关系模型 的主要区别在于,引用是逻辑的,而不是物理的。
- 而在 pre-relational 系统中,物理 记录通过物理指针关联,例如
ID (INTEGER; GUID; UUID; etc)
,在关系范式中,逻辑 行(不是记录)由关系键关联。 - Relational Key需要由数据组成,是逻辑的,不是物理的(物理指针不是数据)。
记录 ID 在关系数据库中没有用处。他们的目的是传播“理论家”的理解:
- 让你迷惑
- 保持 1970 年前的状态,pre-relational,身体心态
- 从而防止关系思维和数据库
至少,每个这样的 Record ID
将是一个 additional 列和一个 additional 索引。
- Additional,不是,而是,因为逻辑数据(Keys)上的索引是数据完整性所必需的,否则你会有重复且不完整。
因此,您的要求提升为关系型,每列少一列和索引table:
备注
我所有的数据模型都在 IDEF1X 中呈现,这是关系数据库建模的标准。
- IDEF1X Introduction 初学者
- IDEF1X Model Anatomy 有经验的人,正在寻求复习
2 个必需的关系键
第二个需要了解的是关系键的完整性特征,您不会拥有它,因为“理论家”很幸运地没有意识到它。这是与问题相关的解释,而不是完整的教程。假设您确实需要数据完整性和关系完整性( 关系模型 提供的逻辑特征(与引用完整性不同,后者是由 SQL 提供的物理设施)。我们将需要它,以便控制(提供完整性)数据层次结构中所需的数据结构 'lower',用于“存储”。
- 虽然上述[1]对于那个目的是正确的,但我们还有一个额外的要求:我们需要
FieldType
inFormField.PK
这样它就可以迁移到它的 child tables. - 但是当然,我们一定不能从[1]上面的逻辑
FormField.PK
中减去。如果我们这样做,它将失去其完整性和意义。 - 所以我们:
- 添加所需的关系键 [2]
- 使它成为主键因为那是被迁移的
- 我们通过将其设为 备用键 来维护原始主键 [1]
- 将关系
FieldType::FormField
从 Non-Identifying 更改为 Identifying.
3 关系解决方案 • 子类型
现在解决方案很简单。
I see two solutions to this [A] or [B]
肯定是 [B],通过一些改进使其达到标准。
[A] 是一个没有完整性的非规范化混乱,这将是维护的噩梦。 Null 是非常有问题的,外键中的 Null 是自杀。
but [B] will require a lot more complexity when storing and fetching data, since the program has to know the type, use the right table, etc etc.
存储空间
完全没有。
- 耐心等待,解决方案如下
- 您清楚地了解数据(“存储”)table必须键入(或强制转换)
- 它需要一个普通的Exclusive Subtype结构。 (自 1960 年代以来,我们在 DBMS 中就有了子类型。)
- 我已经在 Basetype 中提供了输入(转换)控件
- 首先,通过在parent
FormField
主键中插入FieldType
,这样它就会被迁移 - 其次,通过使用Non-Identifying关系,使其不在child
UserField
的Key中
- 但它仍然是 non-key 列,并且它可用作 Basetyp 的 Discriminator
- 首先,通过在parent
- 现在,可以理解为什么 [1][ 和 [2] 必须先实施了
- 看看您是否能欣赏关系键提供的关系完整性(与参照完整性不同):
UserField
是存在于FormName
中的- 逻辑数据完整性级别,这是关系数据库中的标准票价,在“理论家”以欺诈方式销售的 pre-relational 物理(
Record ID
基础)归档系统中无法实现关系
CONSTRAINED
到FieldNames
- Subtype Exposition 初学者
此外,您应该使用 ACID 事务(自第一个版本以来 SQL 合规性的要求),而不是直接从程序中使用 INSERT/UPDATE/DELETE
。这将简化应用程序和程序代码,并消除其他数以千计的错误。如果按照 OLTP 标准实施,它可以最大限度地减少锁定和争用,并减少(如果不能消除)死锁。
检索
不是真的。可能不简单,绝对不复杂,但就是straight-forward.
- 使用
VIEW
(这是VIEWS
的经典用法)- 用于检索已知子类型:
每个子类型一个视图 - 用于检索未知子类型:
任何子类型的一个视图(带有UNION
)(这将return每个基本类型只有一个,无论它是哪个子类型)
(这不是“超类型”,anti-relational 深受“理论家”喜爱的概念,天哪。)
- 用于检索已知子类型:
- 这将使 SQL 编码也 straight-forward。