这是我多年来在多个地方看到的情景; 我想知道是否有其他人遇到过比我更好的解决方案......
我公司销售的产品数量相对较少,但我们销售的产品是高度专业化的(即为了选择给定的产品,必须提供大量的细节).问题在于,虽然选择给定产品所需的细节数量相对恒定,但所需的细节类型在不同产品之间差异很大.例如:
产品X可能具有识别特征,如(假设)
'颜色',
'材料'
'平均失败时间'
但是产品Y可能具有特征
'厚度',
'直径'
'能量源'
在创建同时使用产品X和产品Y的订单系统时,问题(其中之一,无论如何)是订单行必须在某个时候引用它"销售"的内容.由于产品X和产品Y在两个不同的表中定义 - 并且使用宽表格方案的产品非规范化不是一种选择(产品定义非常深) - 很难看到在这样的定义订单行的明确方法订单输入,编辑和报告的实用方式.
我过去曾经尝试过的事情
创建一个名为"Product"的父表,其中包含Product X和Product Y的通用列,然后使用"Product"作为OrderLine表的引用,并创建一个FK关系,将"Product"作为Product X表之间的主要副本和产品Y.这基本上将'Product'表作为OrderLine和所有不同产品表(例如Products X和Y)的父级.它适用于订单输入,但会导致订单报告或编辑出现问题,因为"产品"记录必须跟踪它是什么类型的产品,以确定如何将"产品"加入其更详细的子产品,产品X或产品Y. 优点:保持关键关系. 缺点:在订单行/产品级别进行报告,编辑.
在订单行级别创建"产品类型"和"产品密钥"列,然后使用一些CASE逻辑或视图来确定该行所引用的自定义产品.这类似于第(1)项,没有通用的"产品"表.我认为这是一个更"快速和肮脏"的解决方案,因为它完全取消了订单行和产品定义之间的外键.优点:快速解决方案. 缺点:与第(1)项相同,加上丢失的RI.
通过创建公共标头表并为自定义属性使用键/值对来均匀化产品定义(OrderLine [n] < - [1] Product [1] < - [n] ProductAttribute). 优点:保持关键关系; 没有关于产品定义的含糊不清. 缺点:报告(例如,检索具有其属性的产品列表),属性值的数据类型,性能(获取产品属性,插入或更新产品属性等)
如果其他人尝试了更多成功的不同策略,我肯定希望听到它.
谢谢.
如果您想保持数据完整性,并且您的产品类型相对较少且很少添加新产品类型,那么您描述的第一个解决方案是最好的.这是我在你的情况下选择的设计.仅当您的报告需要特定于产品的属性时,报告才会很复杂.如果您的报告只需要公共Products表中的属性,那就没问题了.
您描述的第二个解决方案称为"多态关联",它并不好.您的"外键"不是真正的外键,因此您不能使用DRI约束来确保数据完整性.OO多态性在关系模型中没有类比.
您描述的第三个解决方案,涉及将属性名称存储为字符串,是一种称为"实体 - 属性 - 值"的设计,您可以说这是一个痛苦且昂贵的解决方案.无法确保数据完整性,无法使一个属性为NOT NULL,无法确保给定产品具有某组属性.无法针对查找表限制一个属性.在SQL中无法进行许多类型的聚合查询,因此您必须编写大量应用程序代码来执行报告.只有在必须时才使用EAV设计,例如,如果您拥有无限数量的产品类型,则每行的属性列表可能不同,并且您的架构必须经常适应新的产品类型,而无需更改代码或架构.
另一种解决方案是"单表继承".这使用了一个非常宽的表,每个产品的每个属性都有一列.在与给定行上的产品无关的列中保留NULL.这实际上意味着您不能将属性声明为NOT NULL(除非它在所有产品的公共组中).此外,大多数RDBMS产品对单个表中的列数或行的总宽度(以字节为单位)有限制.因此,您可以通过这种方式表示的产品类型数量有限.
存在混合解决方案,例如,您可以在列中正常存储公共属性,但在Entity-Attribute-Value表中存储特定于产品的属性.或者,您可以在Products表的BLOB列中以其他结构化方式(如XML或YAML)存储特定于产品的属性.但是这些混合解决方案受到影响,因为现在必须以不同的方式获取某些属性
对于这种情况的最终解决方案是使用语义数据模型,使用RDF而不是关系数据库.这与EAV有一些共同点,但它更加雄心勃勃.所有元数据都以与数据相同的方式存储,因此每个对象都是自描述的,您可以像查询数据一样查询给定产品的属性列表.存在特殊产品,例如Jena或Sesame,实现此数据模型以及与SQL不同的特殊查询语言.