当您开始进入“用户定义的字段”时(通常在错误跟踪器,客户资源管理和类似的业务工具中发现),它们没有用包含数十亿个字段的表来支持(如果有的话,那可能是一个问题)它自己的)。
相反,您找到的是“ 实体属性值”表设计以及用于管理有效属性的关联管理工具。
请考虑下表:
+ -------------- +
| 东西
| -------------- |
| id |
| 类型
| desc |
| attr1 |
| attr2 |
| attr3 |
| attr4 |
| attr5 |
+ -------------- +
这是在您添加了一些属性之后。而不是attr1
假装它读取artist
或tracks
或genre
或事物具有的任何属性。而不是5,如果是50,该怎么办。显然,这是无法控制的。它还需要更新模型并重新部署应用程序以处理新字段。不理想。
现在考虑以下表结构:
+ -------------- + + --------------- + + ------------- +
| 东西 | something_attr | | attr |
| -------------- | | --------------- | | ------------- |
| id | <--- + | something_id(fk)| +> | id |
| 类型 | attr_id(fk)| +-+ | 名称|
| desc | | 价值| | |
+ -------------- + + --------------- + + ------------- +
您已经了解了它的基本领域。您还有两个表。一种具有属性。每个字段在attr
表中都是一行。然后有thing_attr
一对与thing
表和attr
表相关的外键。然后,它具有一个value字段,您可以在其中存储该实体的字段的任何值。
现在,您有了一个结构,可以在运行时更新attr表,并且可以动态添加(或删除)新字段,而不会对整个应用程序产生重大影响。
查询稍微复杂一点,验证也变得更加复杂(时髦的存储过程或所有客户端)。这是设计上的折衷。
还考虑以下情况:某天您需要进行迁移,然后回到应用程序中,发现现在有比原来分配的架构多六个左右的属性。这使得进行丑陋的迁移和升级时,如果正确使用实体属性值表,则可以更清洁。(并不总是,但是可以。)
在运行时仅修改架构有任何弊端吗?如果用户认为事物需要新属性,是否只需向表中动态添加一列?
如果您使用适当的nosql数据库风格,则可以这样做(请注意,适当的nosql风格可能是键值存储,也就是上述关系型的EAV表)没有太多麻烦。 但是,它附带了针对nosql的所有折衷方案,其他地方对此进行了详细介绍。
如果要使用关系数据库,则需要具有架构。动态添加列意味着以下情况的某些子集成立:
- 您正在执行元数据库编程。您可能无法执行类似的操作
select *
,然后执行一些复杂的代码来找出数据实际是什么(请参阅Java的ResultSetMetaData),然后将其存储在映射中(而不是使用一个不错的ORM将该列清晰地映射到该字段)。或其他一些数据类型-但代码中的字段不是很好)。这样一来,您就可以放弃传统方法所具有的相当多的字体和拼写安全性。
- 您可能已经放弃了ORM。这意味着您要为所有代码编写原始sql,而不是让系统为您完成工作。
- 您已经放弃了进行全新升级的准备。当客户添加您的下一个版本也使用的同名字段时,会发生什么情况?在婚介站点中,
hasdate
已经定义了要添加用于存储时间戳记的字段的升级,hasdate
并且为布尔值定义了成功的匹配...,并且升级中断。
- 您相信客户不会通过使用某些保留字来破坏系统,该保留字也会在某些地方破坏您的查询。
- 您已将自己绑定到一个数据库品牌。不同数据库的DDL不同。数据库类型是最简单的例子。
varchar2
vs text
之类的。您添加列的代码将在MySQL上运行,但不适用于Postgres或Oracle或SQL Server。
- 您是否相信客户能够很好地添加数据?当然,EAV远非理想之选,但是现在您有一些开发人员没有添加的可怕的晦涩的表名,索引类型错误(如果有的话),并且在代码中没有添加约束的地方如此等等。
- 您已向运行该应用程序的用户授予了架构修改权限。当您只限于SQL而不是DDL时,就不可能使用Bobby Drop Tables小表(请确保您可以这样做
delete * from students
,但您不能以不好的方式弄乱数据库)。模式访问可能因意外或恶意活动激增而出现问题的数量。
这实际上归结为“不做”。如果您确实要这样做,请使用EAV表结构的已知模式或完全专用于该结构的数据库。不要让人们在表中创建任意字段。头痛是不值得的。