数据库模式,可以支持专门的属性

我需要存储一组实体,其中有几个专门的版本。 他们有一些共同的属性,但专门的属性包含特定于该实体的属性。

解决scheme

数据存储是关系数据库pipe理系统,这不是讨论:-)具体来说,它是Microsoft SQL Server 2005。

我可以很容易地为公共属性创build一个表格,然后为每个专业版本创build一个表格。 但是,稍后可能需要将新实体添加到解决scheme中,而我不想同时维护对象模型数据库模式。

另一个想法是创build一个表

reading(<common properties>, extended_properties) 

并使extended_properties字段成为某种扩展属性的序列化。 我在想JSON或XML。 我很可能会使用ORM框架,但我还没有决定。 无论哪种方式,来自reading的专用实体的对象表示可以从extended_properties字段暴露包含parsing的键/值对的字典{extended_property_name, value}

从http://msdn.microsoft.com/en-us/library/ms345117(SQL.90).aspx可以看出,XML字段与这些模式结合在一起给出了DBMS中types化XML的概念。 此外,在extended_properties字段中涉及XML内容的查询也可以考虑到这些。

我想要的是

关于我的解决schemebuild议的反馈,主要是对reading表和序列化扩展属性的build议。

另外,我认识到这是关系数据库pipe理系统与基于关键/值的商店相比的局限性之一。 但是,必须有一些build模技术来适应这一点。

任何反馈非常感谢!

安德斯,不要放弃任何完整性或硬度,例如types安全。

(响应来临)。

@Anders。 不,一点也没有,子types是好的(问题是你使用哪种forms,什么是dis /优点)。 不要放弃任何力量或诚信或types的安全或检查或DRI。 您select的表单将需要额外的检查,也许需要一些代码(取决于您的平台)。

这个问题经常出现,但是寻找者总是有一个狭窄的视angular。 我不断从一个不变的集合中做出相同的陈述(一个子集)。 这个想法是评估所有的选项。 所以我正在写一个文档。 不幸的是它花费的时间更长 也许4页。 还没有准备好发布。 但是图表完成了,我认为你在球上,你可以马上使用它。

警告:经验丰富的项目build设工程师
道路不适合商业或高Eek因素的读者

链接到正在构build的文档中的四个替代数据模型◀ 。 为地板上的混乱道歉; 我会尽快清理。

对于不熟悉关系数据库build模标准的人员, 链接到IDEF1X表示法◀

  1. 它们都是关系型的,完整的完整性。

  2. 6NF选项。 现在的关系(SQL)不支持6NF; 它不禁止它,它只是不提供5NF➔6NF结构。 所以你需要build立一个小目录,有人称之为“元数据”。 真的,它只是标准SQL目录(sys表)的扩展。 每个选项都要模拟所需的控制级别。

  3. 基本上EAV做得很好,完全控制和完整性(types安全,声明参考完整性等),而不是通常的混乱。

您可能对这些相关的问题/答案感兴趣(特别是查看数据模型):

多固定与抽象灵活

数据库模式相关的问题

“简单”的数据库devise问题

对评论的回应

…这样,我们可以轻松地抓住与给定的特定types实例关联的“Comment”行。 这是做到这一点的方法,或者我会后悔这个决定吗? 还有其他的模式吗?

不明白你的意思。 注释,注释,地址,最终被用在许多表中,因此正确的方法是将它们归一化; 提供一张评论表; 这是从任何需要它的表引用的。 这是一个通用的评论表。 它用于产品(超types),因为您陈述了任何产品。 它可以很容易地用于某些产品子types,而不是其他types。 在这种情况下,FK将在所述产品子types中。

您的数据模型

Product 5NF /子types示例中ProductType表的用途是什么? 它是否包含与每个专用产品相对应的行,例如ProductCPU? 我认为这表明基础产品是专业化的。

(图中小的严重错误,更正。)

对,就是这样。

在标准关系术语中(不是不受控制的混乱数据库),ProductType是鉴别器 ; 它确定哪些产品子types适用于本产品。 告诉你需要join哪个产品子types表。 这两个一起构成一个合理的产品。 不要忘记生成Views,每个ProductType一个。

  • (对于四种数据模型中的每一种,都要评估产品types如何变化,确切地说它扮演了什么angular色。)

  • “泛化 – 专业化”是所有的巨无霸,OO术语; 而不是越过界线,并学习关系已经有30年的能力了。 如果你学习关于关系的一点,你将拥有全部的力量。 否则你只能被限制在非常有限的OO方法中(Ambler和Fowler有很多答案)。 请阅读这篇文章 ,从12月11日起。 关系数据库模型实体,而不是对象; 不是class级。

例如,添加新产品时,您需要提供可以添加哪些产品types的下拉select。 根据这个select,可以推断出哪些表格可以放入数据。正确吗? 我很抱歉谈论应用程序代码,但我只需要把它放到angular度来看

是。 接下来要提供什么页面(有字段),供用户input数据。

谈论将使用Rdb的应用程序代码没有问题,他们像丈夫和妻子(不是丈夫和奴隶)一起走。

  • 对于你的OO类,一旦完成了Rdb的build模,将类树映射到Rdb,独立于将使用它的任何应用程序。 而不是相反。 而不依赖于一个应用程序。

  • 忘记“坚持”,它有许多问题(丢失的更新,损坏的数据完整性,有问题的debugging,大规模的争夺等)。 对Rdb的所有更新都应该在交易中进行,符合ACID的要求可以使用30年,但Fowler和Ambler尚未阅读。 通常这意味着一个存储过程预处理。

判别式就是我们前面所build立的一个types表的FK。 它表示哪个规格。 子types的基types遵守。 但是判别式表格包含了什么?

这是从数据模型不清楚吗? ProducType CHAR(1)(2). Name Char(30) (2). Name Char(30)

可以是一个友好的显示文本,说明用户界面的types,

是的,除其他外,如控制,限制等,消除编码或报告时的歧义。

但它是否也包含确切的表名包含专门的types?

不会。这样做太过于物理化,不能放入数据中。 原则上不允许。

但这不是必要的。

假设我对ID = 1的产品感兴趣,它有一个判别式,表明它是一个ProductCPU。 你将如何去从你的应用程序代码检索这个ProductCPU?

如果你使用提供的模型,并且将它(所有表)作为类,正确地实现,那么这很容易。你请求的例子不会使用Views(这是列表和更通用的用法)。 伪代码将是:

  • 给定ProductId (子types未知,因此你不应该坐在一个子types特定的窗口),只加载Product超types
  • 基于Discriminator Product.ProductType ,设置指标等,并加载适用的子types, ProductCPU; ProductMemory; ProductDisk; ProductTape ProductCPU; ProductMemory; ProductDisk; ProductTape ProductCPU; ProductMemory; ProductDisk; ProductTape ; 等等

  • 我已经看到(也不同意)一次加载给定ProductId所有子types的OO方法:一个子types是有效的; 其余的都是无效的 该代码仍然必须将自身限制为基于Product.ProductTypeProduct的有效类。

或者,例如。 在上下文的情况下,用户正坐在特定于子types的窗口中,例如, ProductCPU ,设置该类,并请求ProductId xxx。 然后使用ProductCPU视图。 如果它返回零行,它不存在。

  • 可能有ProductDisk xxx,但不是ProductCPU xxx。 如何处理这个问题,无论您是否指定了产品“XXX”,但它不是一个CPU,或者不是,取决于应用程序的要求。

对于应用程序填充网格的列表,不考虑ProductId ,使用视图(每个)来加载每个网格。 该SQL基于联接,​​并不需要引用ProductType

我会亲自为“为公共属性创build表格,然后为每个专业版本创build表格”方法。

原因:你说你的实现将在RDBMS中完成,这是不可谈判的。 精细。 在数据库字段中像非序列化哈希表一样转储非结构化的blob-like的东西也违背了RDBMS的devise原则,所以除非你认为将* extended_properties *字段视为不透明blob,就像一个GIF或另一个二进制对象。

换句话说,忘记(高效地)查询“ 扩展属性 COLOR = RED的所有对象”。

您所遇到的问题(在关系型数据库中描述OO分类)绝对不是新的。 看看这个 ,对这个选项进行深入的描述。

这是gen-specdevise模式的典型例子。 Gen-spec在每个关于对象build模的教程中都有介绍,因为它是通过inheritance来处理的。 在关系数据build模教程中经常跳过它。 但是很好理解。

做一个关于“泛化专业化关系build模”的网站。 你会看到关于如何为普通类设置单个表的几篇文章,以及为每个特定类设置一个表。 这些文章将帮助您进行外键devise。 特别是每个专业表的主键都是双重职责。 这也是广义表的一个外键。

如果你习惯对象build模,这对你来说看起来不是很熟悉。 但是你会发现它运作良好。 大多数文章提供的解决scheme都不是dynamic的,所以每当发现一个新的专用子类的时候,你就必须做一些DDL。

这里有五个来自SO的例子。 使用哪一个将取决于您正在解决的实际问题以及您的偏好。

一般来说,我会build议反对序列化数据到数据库字段。

如果一个实体的专用版本相对较less,那么你可以简单地使用子types,就像这些例子: 例子一例子二

对于大的属性值,或者如果要dynamic定义属性(没有模式更改),请查看观察模式的实现,如下例所示: 示例三示例四 ; 或者按照例5所述的所谓的第六种正常forms。 请注意,“观察模式”是第六届NF的简化版本。

这种技术在devise和性能方面可能会遇到一些问题,但是您似乎需要灵活的妥协。

不必为每个属性创build特定的表格或新的字段,而是可以为独特的属性设置一个表格(而且这将会非常大):

 Unique_Property_ID , FK_To_Some_Entity (Not sure what entity these link to: customers, bills, etc.) , Property_Type Not the data type but a link to a table describing this entity) , Property_Value (Difficult to determine if all of your values will be of the same type: string, int, date, etc.) 

示例:二手车经销商需要跟踪不同品牌和型号的附加物(他们永远不知道他们会得到什么)。 几条logging可能如下所示:

 VehicleID Property Type Property Value 3 Sound System Bose 3 Hybrid System Battery 7 Starter Type Hand Crank 7 Passenger Seat Rumble 9 Starter Type Kick Start 9 Passenger Seat Side Car 

每个Set |的每个值 属性types会有自己的logging。

另一个问题是,当你想要每个属性被表示为一列,你需要转置这个表。