配置数据:单行表与名称-值对表


64

假设您编写了一个可由用户配置的应用程序。为了将该“配置数据”存储到数据库中,通常使用两种模式。

  1. 单行表

      CompanyName  |  StartFullScreen  |  RefreshSeconds  |  ...
    ---------------+-------------------+------------------+--------
      ACME Inc.    |        true       |       20         |  ...
    
  2. 名称-值对

      ConfigOption   |   Value
    -----------------+-------------
     CompanyName     | ACME Inc.
     StartFullScreen | true (or 1, or Y, ...)
     RefreshSeconds  | 20
     ...             | ...
    

我已经看到了两种选择,它们都有明显的优缺点,例如:

  • 单行表限制了您可以拥有的配置选项的数量(因为通常限制一行中的列数)。每个其他配置选项都需要更改数据库架构。
  • 在名称/值对表中,所有内容都是“字符串类型的”(您必须对Boolean / Date / etc。参数进行编码/解码)。
  • (还有很多)

开发社区内部对于哪种选择更可取达成共识?


2
“垂直”方法没有理由不能使用不同的数据类型。每行添加一个int,float和text列。使用类型特定的功能(例如'SaveConfigInt('field',n)')从其中保存/加载值
GrandmasterB

4
有一个很好的StackOverflow问题问这个问题,最重要的答案给出了这两种方法的利弊。stackoverflow.com/questions/2300356/…–
凯文

1
方法3:具有简单数据交换格式(如JSON或YAML)的单列/单行。结合了两种方法的优势。
schlamar

如何将单行表与包含xml / json的复杂数据一起使用,例如<config> <CompanyName> ACME Inc。</ CompanyName> <StartFullScreen> true </ StartFullScreen> 20 <RefreshSeconds> </ RefreshSeconds> </ config>和验证业务层中的对象?
约翰

1
@John:好主意,如果需要层次结构。如果不是,那只是增加了复杂性的选择2。
Heinzi '16

Answers:


15

我个人比较喜欢单行表。确实,它的灵活性较差,除非您期望动态行为,否则在以后需要时添加额外的列是完全可以接受的。从某种意义上讲,这等效于在编程时使用字典/映射保存名称/值对与拥有类成员。当然,这不是一个完美的隐喻,但是考虑到它的许多优点和缺点都是平行的。

那么,您会在类成员上使用字典/地图吗?除非您有理由认为要表示的数据量完全可适应,否则就可能不是这样,就像拥有名称/值对表一样。


如果要存储的数据是用户定义的怎么办?例如,想到一个用户可以通过指定字段标签,它将保存的数据类型等来创建“字段”的UI。这意味着从代码执行DDL语句。您还会选择选项1吗?
devanalyst

1
@devanalyst不,如果数据可能会在组件之间变化,那么尝试创建一个静态表来表示它就没有意义。在这种情况下,最好使用第二个选项。
尼尔

12

我通常会选择选项2但要有多个列来强制执行数据类型

ConfigOption   |   textValue    |   DateValue   |   NumericValue

选项1的其他好处是,您可以通过添加Active列来轻松地“交换”整个配置。


如果要允许禁用配置(对于选项1),请至少使其成为activatedOn时间戳记,以便您可以知道何时激活。如果要使用选项2 ...如果最终将值存储在列中(或者是oracle,则(显然)null和空字符串等效)会发生什么?
Clockwork-Muse 2012年

1
@ X-Zero,通常是出于测试目的而存储多个配置的,但是时间戳不会受到伤害。配置维护,获取值的调用将知道要检查的列,如果您确实想要,则可以为数据类型添加一列。但是我认为这已经结束了……
Morons 2012年

5
EATV(实体-属性-类型-值)模式破坏了第三种形式。“类型”列仅通过“类型”列所描述的“值”列与表的主键间接相关。另外,动态类型的存储和实例化并不能解决很多问题。如果GetConfigValue()方法可以返回任何类型,则它必须返回Object(或以某种方式获得期望的类型),并且仍必须在运行时对其进行评估。
KeithS 2012年

5
每当我在看过的软件中实现选项1时,都必须将其转换为选项2。随着时间的推移,选项2更易于维护,只需花一点时间就可以第一次正确实施。选项1易于快速实施,但是随着时间的推移,维护工作将变得很糟糕,除非您的软件很小,没有增长的机会。
Jimmy Hoffa 2012年

8

对我而言,您使用单排还是EAV取决于您如何消费它们。

EAV的强大之处在于,无需更改结构即可添加新数据。这意味着,如果您想要一个新的配置值,只需将其添加到表中并将其拉出到代码中所需的位置即可,而无需向域,架构,映射,DAL查询中添加新字段等

它的缺点是它只有最简单的结构,要求您悲观地处理数据。每次使用任何配置值都必须期望该值不存在或没有正确的格式,并在不存在时相应地执行操作。配置值可能无法解析为double或int或char。它可以为空。该值可能根本没有一行。解决此问题的方法通常要求特定代码内类型的所有配置值都存在一个有效的“默认”值(极少见;更常见的是,默认值与根本不使用代码一样有问题),或者保留默认值的硬编码字典(每次添加新列时,字典都必须更改,这使EAV存储的主要优点变得毫无意义)。

单个宽行几乎相反。您将其映射到配置对象的单个实例,该实例具有针对每个存在的配置值的字段/属性。您确切地知道这些值在编译时应为哪种类型,如果配置列不存在或没有正确类型的值,则可以在DAL中“快速失败”,从而为您提供了一个基于异常的地方关于配置检索/水化问题。

主要缺点是每个新值都需要进行结构更改。新的DB列,DAL中的新列(映射或SQL查询/ SP),新的域列,所有这些都是正确测试使用情况所必需的。

使用这两种方法中的任何一种的适当情况都是减轻不利因素的情况。对我来说,大多数配置编码情况都要求单行实现。这主要是因为,如果要引入一个全新的配置值来控制程序某些部分的行为,则必须已经更改了代码以使用新的配置值。为什么不弹出配置对象并添加要使用的值?

简而言之,用于存储配置的EAV架构实际上并不能解决它声称要解决的问题,并且针对其提出的问题的大多数解决方法都违反了DRY。


3

我会说,专门针对配置值-与单行一起使用。除非您当前正在进行开发,否则这些列将多久更改一次?

最好是保护的数据类型,而不是为了确保代码的可扩展性,而不是在large(r)版本之间的停机时间内实现。此外,添加或删除单个列几乎是最简单的迁移。创建新的配置选项时,我并不感到头疼。

此外,您说过“用户”可以配置这些选项,而无需设置上限。它们是按用户配置吗?如果是这样,我将更加强烈地认为配置选项应该在列中-每个用户一行。稍后将节省很多维护工作的麻烦。


2

如果您的客户可以处理JSON片段(不仅是数组和字典,还可以是纯字符串,数字,布尔值,空值),那么您可以拥有一个带有选项名称和包含JSON的字符串值的多行表。这样一来,您还可以存储结构化值,并且处理这些值的代码应该已经存在。

如果您的客户端无法处理JSON片段,请获取新客户端。


1

单行优点:定义明确。缺点:更改配置可能很麻烦。数据库迁移等。

Entity-Value Pros:超级灵活,支持演进您的配置。缺点:参照完整性?在对代码执行任何操作之前,需要更多检查代码以查看该属性是否存在。

我将采用由非关系数据库(如Mongo)支持的方法2。如果有什么可以确定的,那就可以改变。


1

同时使用!

找出哪些选项可以具有多个实例,哪些选项是通用的。

单行表(配置)

  id  |  company_name  |  start_fullscreen  |  refresh_seconds  |  ...
------+----------------+--------------------+-------------------+-------
  4   |  ACME Inc.     |  true              |  20               |  ...

名称-值对表(选项)

  name             |  value          | update_time  
-------------------+-----------------+--------------
  generic_option_1 |  Option 1 Value | timestamp    
  generic_option_2 |  Option 2 Value | timestamp    
  generic_option_3 |  Option 3 Value | timestamp    
  configuration    |  4              | timestamp    
  ...              |  ...            | ...          

我认为这更灵活。

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.