在SQL Server数据库中使用单行配置表。

Using a Single Row configuration table in SQL Server database. Bad idea?

在开发购物车应用程序时,我发现需要根据管理员的偏好和要求保存设置和配置。此信息可以是来自公司信息、航运帐户ID、Paypal API密钥、通知首选项等的任何信息。

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

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

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


我以前用过两种方法——一个单行表和一个键/值对表——每种方法都有正负两种。

单行

    百万千克1正值:值存储在正确的类型中百万千克1百万千克1正面:在代码中更容易处理(由于以上原因)百万千克1百万千克1正值:可以分别为每个设置指定默认值百万千克1百万千克1负:需要更改架构才能添加新设置百万千克1百万千克1否定:如果有很多设置,桌子会变得很宽百万千克1

型键/值对

    百万千克1正面:添加新设置不需要更改架构百万千克1百万千克1正:表架构很窄,有多余的行用于新设置。百万千克1百万千克1负:每个设置都有相同的默认值(空/空?)百万千克1百万千克1否定:所有内容都必须存储为字符串(即nvarchar)百万千克1百万千克1否定:当处理代码中的设置时,您必须知道什么是类型的设置并强制转换它百万千克1

单行选项是迄今为止最容易使用的选项。这是因为您可以在数据库中以正确的类型存储每个设置,而不必在代码中存储设置的类型及其查找键。

我使用这种方法关注的一件事是在"特殊"的单行设置表中有多行。我克服了这个问题(在SQL Server中):

    百万千克1添加默认值为0的新位列百万千克1百万千克1创建检查约束以确保此列的值为0百万千克1百万千克1在位列上创建唯一约束百万千克1

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


您应该为信息类型和信息值(至少)创建一个带有列的表。这样可以避免每次添加新信息时都必须创建新列。


一行就行了;它甚至有很强的类型:

1
2
3
show_borders    bit
admin_name      VARCHAR(50)
max_users       INT

一个缺点是它需要模式更改(alter table来添加新设置。另一种选择是规范化,在规范化中,您最终得到一个类似以下的表:

1
2
pref_name       VARCHAR(50) PRIMARY KEY
pref_value      VARCHAR(50)

它的类型很弱(所有内容都是varchar),但是添加新的设置只是添加一行,这是只需数据库写入访问就可以做到的。


就我个人而言,如果这是可行的话,我会将它存储在一行中。要将其存储在SQL表中,是否执行了过度终止操作?很可能,但这样做没有真正的伤害。


在规范化方法中添加新的配置参数时,您当然不必更改模式,但是您可能仍然需要更改代码来处理新的值。

在您的部署中添加一个"alter table"对于单行方法的简单性和类型安全性来说并不是一个大的折衷。


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

存储配置和/或用户首选项类型信息的一种方便方法是XML。许多DBMS支持XML数据类型。XML语法允许您随着配置的发展扩展描述配置的"语言"和结构。XML的一个优点是它对层次结构的隐式支持,例如,允许存储配置参数的小列表,而不必用编号后缀来命名这些参数。XML格式的一个可能缺点是,搜索和通常修改这些数据不像其他方法那样直接(没有什么复杂的,但没有简单/自然的)

如果您希望保持更接近关系模型,那么实体属性值模型可能就是您所需要的,其中单个值存储在一个表中,该表通常如下所示:

1
2
3
4
5
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是表的外键,其中定义了每个可能的属性("配置参数"(在您的情况下)),例如

1
2
3
4
5
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允许您标识一些"拥有"这些不同属性的实体。在您的情况下,它可能是一个用户ID,甚至是隐式的,如果您只有一个配置需要管理的话。

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


键和值对类似于可以存储配置设置的.NET app.config。

因此,当您想要检索值时,可以执行以下操作:

1
2
3
SELECT VALUE FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';

实现这一点的一种常见方法是让一个"属性"表与一个属性文件齐鸣。在这里,你可以存储你所有的应用程序常量,或者不是你需要的那些常量。

然后,您可以根据需要从这个表中获取信息。同样,当您发现要保存其他设置时,可以将其添加到中。下面是一个例子:

属性_entry_table

1
2
3
4
5
6
7
[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,您就知道它是什么首选项。

当有一天,您需要在这里存储非管理信息时,属性类型就在手边了。

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

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

重点是,您不想将此用于与购物车相关的内容。将业务逻辑保存在定义良好的关系表中。属性表仅用于系统信息。


将键列设为varchar,值列设为json。1是数字,而"1"是字符串。truefalse都是布尔值。也可以有对象。


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

ID - int (11)

name - varchar (64)

value - text

在创建新的配置列、更新它或读取它之前,我要做的是序列化"value"!这样我就确定了类型(好吧,php是:)

例如:

b:0; is for BOOLEAN (false)

b:1; is for BOOLEAN (true)

i:1988; is for INT

s:5:"Kader"; is for a STRING of 5 characters length

希望这有帮助:)


通过为每个主要类型添加一列,并在一列中告诉您数据在哪个列中,可以在不进行转换的情况下进行键/值对。

所以你的桌子看起来像:

1
2
3
4
5
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,

它使用了更多的空间,但最多只能使用几十个属性。您可以使用列_Num值的case语句来拉入/加入右字段。


我不确定单个行是否是配置的最佳实现。对于每一个配置项,最好有一行两列(configname、configvalue),尽管这需要将所有值强制转换为字符串并返回。

无论如何,使用一行进行全局配置没有任何危害。在DB(全局变量)中存储它的其他选项更糟。您可以通过插入第一个配置行来控制它,然后禁用表上的插入以防止多行。