2008-09-23 96 views
7

这是我多年来在多个地方见过的场景;我想知道是否有其他人遇到了比我更好的解决方案...设计一个'订单'模式,其中有不同的产品定义表

我的公司销售的产品相对较少,但是我们销售的产品是高度专业化的(即为了选择给定的产品,必须提供相当多的细节)。问题在于虽然选择给定产品所需的细节数量是相对恒定的,但细节所需的细节在产品之间差异很大。例如:

产品X可能会识别像(假设)

  • '颜色',
  • '材质'
  • '平均无故障时间'

但产品特点Y可能有特征

  • “厚度”,
  • 创造,利用两个产品X和Y产品的订单系统“直径”
  • “电源”

的问题(他们中的一个,反正)是一个令线路在某些时候必须提及“销售”。由于产品X和产品Y被定义在两个不同的表格中 - 并且使用宽表格方案对产品进行非规范化不是一种选择(产品定义非常深入) - 很难看到明确的方式来定义此类订单行订单输入,编辑和报告是实用的方式。


事情我已经试过在过去

  • 创建一个名为“产品”与产品X和Y产品共同列的父表,然后使用“产品”作为参考OrderLine表,并创建与'产品'的FK关系作为产品X和产品Y的表之间的主要方面。这基本上将'产品'表放置为OrderLine和所有不同产品表(例如产品X和Y)。它对订单输入工作正常,但会导致订单报告或编辑出现问题,因为'产品'记录必须跟踪它的产品类型,以便确定如何将'产品'加入其更详细的子产品X或产品Y. 优点:保留关键关系。 缺点:报告,在订单行/产品级别进行编辑。
  • 在订单行级别创建“产品类型”和“产品密钥”列,然后使用一些CASE逻辑或视图来确定行参考的定制产品。这与项目(1)类似,没有常见的“产品”表。我认为这是一个更“快捷和肮脏”的解决方案,因为它完全消除了订单行和产品定义之间的外键。 优点:快速解决方案。 缺点:与第(1)项相同,加上丢失的RI。
  • 通过创建公用标题表并使用自定义属性的键/值对(OrderLine [n] < - [1] Product [1] < - [n] ProductAttribute))使产品定义均匀化。 优点:保留关键关系;关于产品定义没有歧义。 缺点:报告(检索的产品及其属性的列表,例如),数据属性值的类型,性能(取产品属性,插入或更新的产品属性等)

如果任何人试图一种更成功的策略,我很乐意听到。

谢谢。

回答

2

这可能会让你开始。这将需要一些改进

Table Product (id PK, name, price, units_per_package) 
Table Product_Attribs (id FK ref Product, AttribName, AttribValue) 

这将允许您将属性列表附加到产品。 - 这基本上是你的选择3

如果您知道属性的最大数量,你可以去

Table Product (id PK, name, price, units_per_package, attrName_1, attrValue_1 ...) 

这当然会去数据库规范化,但进行查询更加容易。

我更喜欢第一个选项,因为

  1. 它支持一个任意的属性数量。
  2. 属性名称可以存储在另一个表中,强制执行参照完整性,以便那些该死的加拿大人不会在那里粘住“颜色”并中断报告。
1

您的产品线是否改变过?
如果确实如此,那么为每件产品创建一个表格将会付出高昂的代价,而关键/值对配合的想法将为您提供良好的服务。这是我自然而然地画出的那种方向。

我创建表是这样的:

Attribute(attribute_id, description, is_listed)  
-- contains values like "colour", "width", "power source", etc. 
-- "is_listed" tells us if we can get a list of valid values: 

AttributeValue(attribute_id, value) 
-- lists of valid values for different attributes. 

Product (product_id, description) 

ProductAttribute (product_id, attribute_id) 
-- tells us which attributes apply to which products 

Order (order_id, etc) 

OrderLine (order_id, order_line_id, product_id) 

OrderLineProductAttributeValue (order_line_id, attribute_id, value) 
-- tells us things like: order line 999 has "colour" of "blue" 

的SQL来拉了一起不平凡的,但它不是太复杂要么...而且大部分会被写一次,并保持(在存储过程或数据访问层)。

我们用许多类型的实体做类似的事情。

0

克里斯和AJ:谢谢你的回应。产品线可能会改变,但我不会将其称为“易变”。

我不喜欢第三种选择的原因是它以产品属性值的元数据为代价。它本质上是将列变为行,在进程中丢失了数据库列的大部分优点(数据类型,默认值,约束,外键关系等)。

我实际上已经参与了过去的项目,产品定义以这种方式完成。我们基本上创建了完整的产品/产品属性定义系统(数据类型,最小/最大事件,默认值,'必需'标志,使用场景等。)系统最终运作良好,但是在开销和性能方面存在显着的成本(例如,实现产品可视化的物化视图,用于表示和验证用于产品定义的数据输入UI的定制“智能”组件,用于表示产品的另一个“智能”组件订单行上的实例可定制属性,blahblahblah)。

再次感谢您的回复!

5

您介绍的就是最好的,如果你想保持数据的完整性,如果你有相对较少的产品种类和添加很少的新产品类型的第一个解决方案。这是我在你的情况下选择的设计。只有您的报告需要产品特定的属性时,报告才是复杂的。如果您的报告只需要普通产品表中的属性,就没有问题。

您所描述的第二个解决方案被称为“多态关联”,它是没有好处的。您的“外键”不是真正的外键,因此您不能使用DRI约束来确保数据的完整性。 OO多态性在关系模型中没有类比。

您所描述的第三种解决方案,涉及存储属性名称作为字符串,是一个被称为“实体 - 属性 - 值”的设计,你可以告诉这是一个痛苦和昂贵的解决方案。没有办法确保数据的完整性,没有办法使一个属性不为NULL,也没有办法确保给定的产品具有一定的属性。没有办法限制查找表的一个属性。许多类型的聚合查询在SQL中不可能实现,因此您必须编写大量的应用程序代码才能执行报告。仅当必须使用EAV设计时(例如,如果您拥有无限数量的产品类型,则每行的属性列表可能不同),并且您的模式必须经常适应新的产品类型,而无需更改代码或模式。

另一种解决方案是“单表继承”。这对于每个产品的每个属性都使用一个非常宽的表格和一列。将NULL留在与给定行上的产品无关的列中。这实际上意味着您不能将属性声明为NOT NULL(除非它在所有产品通用的组中)。此外,大多数RDBMS产品对单个表中列的数量或行的总体宽度(以字节为单位)有限制。所以你可以用这种方式来表示你所能代表的产品类型的数量。

混合解决方案存在的,比如,你可以正常,存储共同属性在列,但在实体 - 属性 - 值表的具体产品的属性。或者,您可以在产品表的BLOB列中以某种其他结构化方式(如XML或YAML)存储特定于产品的属性。但是,这些混合解决方案受到影响,因为现在的一些属性必须以不同的方式

对于这样的情况下的最终解决方案是使用语义数据模型中获取,使用,而不是关系数据库RDF。这与EAV有一些共同点,但它更加雄心勃勃。所有元数据都以与数据相同的方式存储,因此每个对象都是自描述的,您可以查询给定产品的属性列表,就像查询数据一样。存在特殊产品,例如JenaSesame,实现此数据模型和与SQL不同的特殊查询语言。

2

没有你忽视的魔力。

您有什么有时被称为“不相交的子类”。有两个子类(ProductX)和(ProductY)的超类(Product)。这对于关系数据库来说是一个难题。 [另一个难题是材料清单。另一个棘手的问题是节点和弧形图。]

您确实需要多态性,其中OrderLine链接到Product的子类,但不知道(或关心)哪个特定的子类。

您没有太多建模选择。你几乎已经识别出每个的不好的特征。这几乎是整个选择的范围。

  1. 将一切推到超类。这是单表方法,其中产品具有鉴别器(type =“X”和type =“Y”)和一百万列。 Product的列是ProductX和ProductY中的列的联合。由于未使用的列将会有空位。

  2. 将所有东西推入子类。在这种情况下,您需要一个与ProductX和ProductY结合的视图。这个观点是加入到创建一个完整的订单。这就像第一个解决方案,除非它是动态构建的,并且不能很好地优化。

  3. 将超类实例加入子类实例。在这种情况下,Product表是ProductX和ProductY列的交集。每个产品都可以在ProductX或ProductY中引用密钥。

没有真正的大胆的新方向。在关系数据库世界观中,这些是选择。

但是,如果您选择改变构建应用程序软件的方式,您可以摆脱这种陷阱。如果应用程序是面向对象的,那么您可以使用一流的多态对象来处理所有事情。你必须从那种笨重的关系处理中映射出来;这会发生两次:一次是从数据库获取东西来创建对象,一次是将对象持久存储回数据库。

优点是您可以简洁而正确地描述您的处理过程。作为对象,与子类关系。

缺点是您的SQL使用简单的批量提取,更新和插入。

当SQL被隔离到ORM层中并作为一种简单的实现细节进行管理时,这将成为一种优势。 Java程序员使用iBatis(或Hibernate或TopLink或Cocoon),Python程序员使用SQLAlchemy或SQLObject。 ORM执行数据库提取和保存;您的应用程序直接操作订单,行和产品。