我正在开发一个编辑器,使其用户能够实时创建“对象”定义。定义可以包含零个或多个属性。属性的名称类型。创建定义后,用户可以创建该定义的对象并设置该对象的属性值。
因此,通过单击鼠标按钮,用户应该是。能够创建一个名为“Bicycle”的新定义,并添加“Numeric”类型的属性“Size”。然后是另一个名为“Text”的属性,名为“Text”,然后是另一个名为“Price”的属性,类型为“Numeric”。完成后,用户应该能够创建一些“自行车”对象,并填写每辆自行车的“名称”和“价格”属性值。
现在,我已经在几个软件产品中看到了这个功能,所以它必须是一个众所周知的概念。当我坐下来试图想出一个DB模式来支持这个数据结构时,我的问题就开始了,因为我希望使用适当的列类型来存储属性值。 IE浏览器。数字属性值存储为数据库中的INT,文本属性值存储为VARCHAR。
首先,我需要一个能保存所有对象定义的表:
Table obj_defs
id | name |
----------------
1 | "Bicycle" |
2 | "Book" |
然后我需要一个表来保存每个对象定义应具有的属性类型:
Table prop_defs
id | obj_def_id | name | type |
------------------------------------
1 | 1 | "Size" | ? |
2 | 1 | "Name" | ? |
3 | 1 | "Price" | ? |
4 | 2 | "Title" | ? |
5 | 2 | "Author" | ? |
6 | 2 | "ISBN" | ? |
我还需要一个包含每个对象的表:
Table objects
id | created | updated |
------------------------------
1 | 2011-05-14 | 2011-06-15 |
2 | 2011-05-14 | 2011-06-15 |
3 | 2011-05-14 | 2011-06-15 |
最后,我需要一个表来保存每个对象的实际属性值,并且该表的一个解决方案是为每个可能的值类型都有一列,例如:
Table prop_vals
id | prop_def_id | object_id | numeric | textual | boolean |
------------------------------------------------------------
1 | 1 | 1 | 27 | | |
2 | 2 | 1 | | "Trek" | |
3 | 3 | 1 | 1249 | | |
4 | 1 | 2 | 26 | | |
5 | 2 | 2 | | "GT" | |
6 | 3 | 2 | 159 | | |
7 | 4 | 3 | | "It" | |
8 | 5 | 3 | | "King" | |
9 | 6 | 4 | 9 | | |
如果我实现了这个模式,那么prop_defs表的“type”列会保持什么?每个映射到列名的整数,varchars只是保存列名?还有其他可能吗?存储过程会以某种方式帮助我吗?用于获取对象2的“name”属性的SQL是什么样的?
答案 0 :(得分:28)
您正在实施名为Entity-Attribute-Value model http://en.wikipedia.org/wiki/Entity-attribute-value_model的内容。
很多人都会说这是一个坏主意(通常我就是其中之一),因为你最后一个问题的答案,“SQL取出的内容......”往往是“厚毛茸茸,讨厌,get get”更糟“。
一旦你允许用户开始在其他对象中嵌套对象,这些批评就会成立,如果你不允许这样做,情况将是可控的。
对于你的第一个问题,“prop_defs表的”type“列会包含什么”,如果你有一个包含{“numeric”,“Any Number”}的类型和描述的表,一切都会更简单,{ “textual”,“String”}等。第一个值是主键。然后在prop_defs中,您的列“type”是该表的外键并保存值“numeric”,“textual”等。有些人会错误地告诉您总是使用整数键,因为它们加速更快,但如果您使用值“数字“,”文本“等你不必加入,最快的JOIN就是你没有做的。
获取单个值的查询将具有CASE语句:
SELECT case when pd.type = "numeric" then pv.numeric
when pd.type = "textual" then pv.textual
when pd.type = "boolean" then pv.boolean
from prov_vals pv
JOIN prop_defs pd ON pv.prop_def_id = pv.id
WHERE pv.object_id = 2
AND pd.name = "Name"
答案 1 :(得分:4)
您必须接受关系数据库不擅长提供此类功能。他们可以提供它,但不擅长它。 (我希望我错了)。关系数据库更适合定义的接口,而不是改变接口。
- EAV表提供动态字段,但性能很差。很糟糕的索引。查询很复杂。它可以在很多情况下完成工作,但是在大桌面上可能会崩溃,很多用户都会遇到系统。
- 具有多个占位符列的“常规”表格可以提高性能,但是您获得了非描述性的列名称,并且可以“添加”列数限制。它也不支持子类型分离。
- 通常在开发时创建/修改表,而不是运行时。我们真的应该歧视在运行时修改数据库吗?也许,也许不是。在运行时创建新表,外键和列可以实现真正的动态对象,同时提供“常规”表的性能优势。但是您必须查询数据库的模式,然后动态生成所有查询。那会很糟糕。它完全打破了表作为接口的概念。