在SQL Server数据库中使用单行configuration表。 馊主意?

在开发购物车应用程序时,我发现我需要根据pipe理员的偏好和要求保存设置和configuration。 这些信息可以来自公司信息,运输账户ID,PayPal API密钥,通知首选项等等。

在关系数据库系统中创build一个表来存储单行似乎是非常不合适的。

什么是适当的方式来存储这些信息?

注意:我的DBMS是SQL Server 2008,编程层是用ASP.NET(在C#中)实现的。

过去我做过两种方法 – 单行表和键/值对表 – 每种方法都有正面和负面的作用。

单行

  • 正面的:值被存储在正确的types中
  • 积极的:在代码中处理更容易(由于上述原因)
  • 正面的:可以单独给每个设置默认值
  • 否定的:需要更改架构来添加新的设置
  • 否定的:如果有很多设置,表格可能变得很宽

键/值对

  • 积极的:添加新的设置不需要模式更改
  • 肯定的:表模式是狭窄的,额外的行被用于新的设置
  • 否定的:每个设置都有相同的默认值(空/空?)
  • 否定的:所有东西都必须作为string存储(即nvarchar)
  • 否定的:在处理代码中的设置时,你必须知道什么types的设置并投射它

单行选项是迄今为止最简单的工作。 这是因为您可以将每个设置以正确的types存储在数据库中,而不必在代码中存储设置的types以及查找键。

有一件事我很关心使用这种方法是在“特殊”单行设置表中有多行。 我通过(在SQL Server中)克服了这个问题:

  • 添加一个默认值为0的新位列
  • 创build一个检查约束来确保此列的值为0
  • 在位列上创build一个唯一的约束

这意味着表中只能存在一行,因为位列必须具有值0,但由于唯一约束,只能有一行具有该值。

您应该创build一个包含信息types和信息值列的表(至less)。 这样可以避免每次添加新信息时都必须创build新的列。

单行将正常工作; 它甚至会有强大的types:

show_borders bit admin_name varchar(50) max_users int 

一个缺点是它需要模式更改( alter table )来添加新的设置。 一种select是正常化,最终得到一个如下表格:

 pref_name varchar(50) primary key pref_value varchar(50) 

这有弱types(一切都是varchar),但添加一个新的设置只是添加一个行,你可以做的只是数据库写访问。

就个人而言,如果这是行之有效的话,我会把它存储在一行中。 矫枉过正将其存储在SQL表中? 很可能,但这样做并没有真正的危害。

在标准化方法中添加新configuration参数时,您当然不必更改模式,但是您仍然可能更改代码以处理新值。

为您的部署添加一个“alter table”似乎不像是单行方法的简单性和types安全性的一个折衷。

正如你所猜测的,除了最简单的情况之外,将所有的configuration参数放在一行中有许多缺点。 这是一个坏主意…

存储configuration和/或用户偏好信息types的便捷方式是使用XML 。 许多DBMS支持XML数据types。 XML语法允许您随着此configuration的发展而扩展描述configuration的“语言”和结构。 XML的一个优点是对分层结构的隐式支持,允许例如存储configuration参数的小列表,而不必使用编号后缀命名这些列表。 XML格式的一个可能的缺点是,search和一般修改这些数据并不像其他方法那样直截了当(没有什么复杂的,但并不那么简单/自然)

如果您希望保持与关系模型的距离更近 ,那么实体 – 属性 – 值模型可能就是您所需要的,因此各个值将存储在一个通常如下所示的表中:

 EntityId (foreign key to the "owner" of this attribute) AttributeId (foreign key to the "metadata" table where the attribute is defined) StringValue (it is often convenient to have different columns of different types IntValue allowing to store the various attributes in a format that befits them) 

因此,AttributeId是一个表的外键,其中每个可能的属性(在你的情况下的“configuration参数”)定义,说

 AttributeId (Primary Key) Name AttributeType (some code S = string, I = Int etc.) Required (some boolean indicating that this is required) Some_other_fields (for example to define in which order these attributes get displayed etc...) 

最后,EntityId允许你标识一些“拥有”这些不同属性的实体。 在你的情况下,它可能是一个UserId,甚至只是隐含的,如果你只有一个configuration来pipe理。

除了允许可能的configuration参数的列表随着应用程序的发展而增长之外,EAV模型将“元数据”(即与属性本身有关的数据)放置在数据表中,从而避免了常见的列名的硬编码当configuration参数存储在单行中时。

Key和Value对与.Net App.Config类似,可以存储configuration设置。

所以当你想要检索你可以做的值时:

 SELECT value FROM configurationTable WHERE ApplicationGroup = 'myappgroup' AND keyDescription = 'myKey'; 

执行此操作的常用方法是对属性文件使用“属性”表。 在这里,你可以存储所有的应用程序常量,或者你不需要那么常量的东西。

您可以根据需要从该表中获取信息。 同样,如果您发现还有其他设置要保存,则可以将其添加进去。下面是一个示例:

property_entry_table

 [id, scope, refId, propertyName, propertyValue, propertyType] 1, 0, 1, "COMPANY_INFO", "Acme Tools", "ADMIN" 2, 0, 1, "SHIPPING_ID", "12333484", "ADMIN" 3, 0, 1, "PAYPAL_KEY", "2143123412341", "ADMIN" 4, 0, 1, "PAYPAL_KEY", "123412341234123", "ADMIN" 5, 0, 1, "NOTIF_PREF", "ON", "ADMIN" 6, 0, 2, "NOTIF_PREF", "OFF", "ADMIN" 

这样你就可以存储你拥有的数据,以及明年还不知道的数据:)。

在这个例子中,您的范围和refId可以用于任何你想要的后端。 所以如果propertyType“ADMIN”有一个范围0 refId 2,你知道它是什么样的偏好。

物业types时,有一天,你需要存储非pipe理员信息在这里以及。

请注意,您不应该以这种方式存储购物车数据,或者查找这个问题。 但是,如果数据是系统特定的,那么你当然可以使用这种方法。

例如:如果你想存储你的DATABASE_VERSION ,你可以使用这样的表。 这样,当您需要升级应用程序时,您可以检查属性表来查看客户端软件的版本。

关键是你不想用这个与购物车有关的东西。 使业务逻辑保持在定义良好的关系表中。 属性表仅用于系统信息。

我不确定单行是否是configuration的最佳实现。 你可能会更好的是每个configuration项有两列(configName,configValue),尽pipe这需要将所有的值转换为string并返回。

无论如何,使用单行进行全局configuration并没有什么坏处。 将其存储在数据库(全局variables)中的其他选项更糟糕。 您可以通过插入第一个configuration行来控制它,然后禁用表上的插入以防止多行。

您可以通过为每个主types和一列添加一列来告诉您数据所在的列,从而无需转换即可执行键/值对。

所以你的桌子看起来像这样:

 id, column_num, property_name, intValue, floatValue, charValue, dateValue 1, 1, weeks, 51, , , 2, 2, pi, , 3.14159, , 3, 4, FiscYearEnd, , , , 1/31/2015 4, 3, CompanyName, , , ACME, 

它使用更多的空间,但最多只能使用几十个属性。 您可以使用case_num值的case语句来拉/join正确的字段。

对不起,我过来了。 但无论如何,我所做的是简单而有效的。 我只需创build一个包含三个()列的表格:

ID – int(11)

名称 – varchar(64)

价值 – 文字

在创build一个新的configuration列之前,更新或读取的操作是序列化“值”! 这样我肯定的types(嗯,php是:))

例如:

B:0; 是B OOLEAN(

B:1; 是B OOLEAN(

I:1988; 是我的 NT

S:5: “卡迪尔”; 是5个字符长度的S TRING

我希望这有帮助 :)

将键列作为varchar和值列作为JSON。 1是数字,而"1"是一个string。 truefalse都是布尔值。 你也可以有对象。