允许用户定义字段是不好的做法吗?


17

一般来说,允许用户在Webapp数据库中创建用户创建的字段是否被认为是不好的做法?

例如,我正在为妻子制作一个房屋库存网络应用程序,而她将要为不同的项目定义自己的字段。我打算允许她创建商品类别,并在这些类别中添加“功能”。功能只是将键/值存储为字符串。这样,例如,如果她有一个名为“音频CD”的类别,则可以为“艺术家”,“曲目”等内容添加功能。但是在另一个“家具”类别中,她可以为诸如“材料”之类添加功能”(木材,塑料等)。然后,任何项目都可以属于一个(或多个)类别,将那些功能添加到该项目中。

我看到的问题是,通过这些功能进行搜索需要字符串比较,没有数据验证等。按照敏捷的方法,也许最好是让她提出新的类别和属性,而我只需要创建新表当我们去。在我的示例中,这是一个很小的用户群(我们当中有2个),并且创建的记录量很小,因此也不错。

一般来说,人们在“现实生活”中如何处理这样的事情?


4
您是否考虑过使用像MongoDB这样的面向文档的数据库?您可以按类型存储一个文档,该文档充当一种模式,也可以编辑(鉴于项目规模较小,可以手动编辑)。
安迪·亨特2014年

@AndyBursh当前postgres的'fun'位之一是'json'数据类型(link)。这种方法将允许用户将用户指定的字段存储在该数据,文档,文档等中,然后将其余字段用于适当索引的对象等。尽管这一切都取决于使用情况,并且很难说这是否适用于特定应用程序。但是有一点要注意。

所有人:精彩的讨论,感谢您的所有见识!@AndyBursh我听说过MongoDB,但从未真正阅读过它。听起来像是另一个可以尝试...的家庭项目
zako42

Answers:


19

当您开始进入“用户定义的字段”时(通常在错误跟踪器,客户资源管理和类似的业务工具中发现),它们没有用包含数十亿个字段的表来支持(如果有的话,那可能是一个问题)它自己的)。

相反,您找到的是“ 实体属性值”表设计以及用于管理有效属性的关联管理工具。

请考虑下表:

  + -------------- +
  | 东西
  | -------------- |
  | id |
  | 类型
  | desc |
  | attr1 |
  | attr2 |
  | attr3 |
  | attr4 |
  | attr5 |
  + -------------- +

这是在您添加了一些属性之后。而不是attr1假装它读取artisttracksgenre或事物具有的任何属性。而不是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不同。数据库类型是最简单的例子。 varchar2vs text之类的。您添加列的代码将在MySQL上运行,但不适用于Postgres或Oracle或SQL Server。
  • 您是否相信客户能够很好地添加数据?当然,EAV远非理想之选,但是现在您有一些开发人员没有添加的可怕的晦涩的表名,索引类型错误(如果有的话),并且在代码中没有添加约束的地方如此等等。
  • 您已向运行该应用程序的用户授予了架构修改权限。当您只限于SQL而不是DDL时,就不可能使用Bobby Drop Tables小表(请确保您可以这样做delete * from students,但您不能以不好的方式弄乱数据库)。模式访问可能因意外或恶意活动激增而出现问题的数量。

这实际上归结为“不做”。如果您确实要这样做,请使用EAV表结构的已知模式或完全专用于该结构的数据库。不要让人们在表中创建任意字段。头痛是不值得的。


4
您还重新发明了数据库。
user253751 2014年

1
@immibis增加了一个层,用户可以在其中进行管理,而不会破坏数据库的其余部分或需要重新部署以更新模型。

1
@immibis EAV多年来在关系数据库领域引起了激烈的争论。从理论上讲,这是没有必要的,但实际上,没有它,您将无法做某些事情。
罗斯·帕特森

1
使用NoSQL方法的@ShivanDragon。文档存储区仅存储文档,不施加模式。这样,添加和删除字段以及解析文档完全超出了数据库本身的范围(并且您已经编写了模型来适应这种情况)。与针对EAV结构的关系数据库妥协相比,这是一组完全不同的妥协。


5

做到这一点很难。

对于像您计划的那样的一次性应用程序,您当然可以为每个字段添加一列,并提供一个UI,使未经培训的用户定义字段比给他们SQL命令行更安全。或者,您可以遵循可怕的Entity-Attribute-Value模式,这是对这种问题的经典(即使有些吓人)响应。构建用于定义EAV字段的UI通常比数据库列复杂得多,查询可能会变得很繁琐,但是对于大量字段(高度稀疏矩阵模式),这可能是获取该字段的唯一方法工作完成。


总结:小项目==吻。敏捷到地面。
Encaitar 2014年

数据库表更新的问题在于,根据数据量和所需索引(自定义字段通常需要搜索功能),更改表的查询可能会花费大量时间。长话短说,MySQL和其他关系数据库根本不是满足这种要求的好方法。
Oddman

0

我最近遇到了类似的事情。

我做了2张桌子。

1: table Objects 
    Id , name, type

他是你的全部对象。设置它的名称。

这个对象的类型是:-对我来说,可用类型是stock,ventory_item和office。

通常的设置是n个项目是子项或库存,这也是办公室的子项,我使用联接表将对象彼此联接

2 table settings 
     organization_Id , title, value , type

设置表包含该特定对象类型的每个字段名称以及值。

办公室的示例属性

位置,电话,工作时间

对于物品

  • 价钱
  • 条码

等等,所有这些属性都由您的模型强制执行,并作为单独的行保存在设置表中(但请使用replace not insert以避免同一字段出现多行)

因此,无论何时我想要一个办公室,我都可以轻松地将其所有关系和设置(其中的设置为object_I(请求的对象))加载到办公室

之后,我将所有行都从设置中移开,仅此而已。

如果我想让某个设置特定于库存中的某个项目(不是全局),则可以设置object_Idd =我来自object_objects关系表并设置设置。

希望您能理解我的意思,当我使用笔记本电脑时,我将尝试重新格式化答案


2
专业提示-不要通过手机在此论坛上发帖。自动更正会使部分帖子不可读。
BobDalgleish 2014年

哈哈不错的观察:)
Zalaboza 2014年

0

允许用户定义字段是不好的做法吗?

不,这不是一个坏习惯。这很普遍。用面向对象的术语,这称为继承。您有一个基类ventoryItem和两个继承的类AudioCD和家具。

一般来说,人们在“现实生活”中如何处理这样的事情?

您必须决定如何将ventoryItem,AudioCD和家具存储在数据库中。

如果简单查询对您来说最重要,而数据库空间/规范化无关紧要,则可以实现“每层表”模式。

如果空间/规范化对您来说最重要,并且更复杂的查询没问题,那么您可以实现“每类型表”模式。

有关更多详细信息,请参见dotnet table-per-type-vs-table-per-hierarchy-inheritanceJava休眠继承


我不知道这是否解决了这个问题。用户没有修改代码以创建新的类
Colin D
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.