And*_*rew 45 sql database database-design
我需要在数据库中存储一系列配置值.我想要存储它们的几种方法是:一个包含2个列(名称,值)的表和每对的行,或者每个配置参数和1行的列表?第一个我只需要添加另一行来添加配置值,第二行我需要在表中添加一列.我应该考虑哪些问题?一个比另一个更有效吗?
Pet*_*ona 26
对于配置数据,我使用键/值结构,每个配置条目有一行.您可能会一次读取此数据并对其进行缓存,因此性能不是问题.正如您所指出的,每次配置密钥更改时添加列都需要更多维护.
SQL擅长建模和操作任意大型的类似(如果不是相同的)结构化数据集.一组配置信息实际上并非如此 - 您有一行数据或者您有多行完全不相关的数据.这表示你只是将它用作数据存储.我说跳过SQL数据模型并简单.
Luc*_*c M 14
还有一个考虑因素:每个配置参数都有一列,您可以轻松拥有版本.每行代表一个版本.
cle*_*tus 13
您应该考虑的第一个问题是:停止考虑检索信息的效率.首先,弄清楚如何有效和正确地建模数据,然后(并且只有这样)才能找到有效的方法.
所以这取决于您存储的配置数据的性质.如果单独的(名称,值)对基本上不相关,则将其存储为每行一个.如果它们相关,那么您可能需要考虑具有多个列的方案.
相关的是什么意思?考虑一些缓存配置.每个缓存都有几个属性:
假设每个缓存都有一个名称.您可以将此数据存储为三行:
<name>_EVICTION
<name>_EXPIRY
<name>_MAX_SIZE
但这些数据是相关的,您可能经常需要一次性检索它们.在这种情况下,拥有一个包含五列的cache_config表可能是有意义的:id,name,eviction,expiry,max_size.
这就是我所说的相关数据.
Ken*_*itt 13
为每个(应用程序)配置设置(或应用程序选项)使用单独行的一个缺点是,您无法将设置值存储在具有适当数据类型的列中.用户可以输入无效类型的数据吗?这与您的申请有关吗?
使用单独列的一个好处是数据库本身的任何代码(例如存储过程,函数等)都可以使用适当数据类型的值,而无需先检查无效值,然后转换为适当的数据类型.
如果您手动部署更改您的应用程序数据库,然后是如果你使用的是EAV的设计是非常轻微更易于部署新的配置设置,但真的有什么节省:
INSERT Options ( ConfigurationSetting, Value )
VALUES ( 'NewConfigurationSetting', NewConfigurationSettingValue )
Run Code Online (Sandbox Code Playgroud)
与:
ALTER TABLE Options ADD NewConfigurationSetting some_datatype
UPDATE Options
SET NewConfigurationSetting = NewConfigurationSettingValue
Run Code Online (Sandbox Code Playgroud)
我鄙视将非字符串值放在字符串列中(又名,不正确的数据类型)。(正如@Kenny Evitt 在上面讨论的那样)
所以我想出了下面的替代方案,它可以垂直并处理正确的数据类型。
我实际上不使用钱和小钱。但为了完整起见,我将它们包括在内。请注意,还有一些其他数据类型
看
https://msdn.microsoft.com/en-us/library/ms187752.aspx?f=255&MSPPError=-2147217396
但以下内容涵盖了大部分内容。
老实说,我只使用 string (varchar(1024))、int、smallint 和 bit ... 99% 的时间。
它并不完美。Aka,你有很多空元组。但是因为你只抓取一次(和缓存),映射到设置对象(在我的世界中的 c# 中)并不困难。
CREATE TABLE [dbo].[SystemSetting](
[SystemSettingId] [int] IDENTITY NOT NULL,
[SettingKeyName] [nvarchar](64) NOT NULL,
[SettingDataType] [nvarchar](64) NOT NULL, /* store the datatype as string here */
[SettingValueBigInt] bigint NULL,
[SettingValueNumeric] numeric NULL,
[SettingValueSmallInt] smallint NULL,
[SettingValueDecimal] decimal NULL,
[SettingValueSmallMoney] smallmoney NULL,
[SettingValueInt] int NULL,
[SettingValueTinyInt] tinyint NULL,
[SettingValueMoney] money NULL,
[SettingValueFloat] float NULL,
[SettingValueReal] real NULL,
[SettingValueDate] date NULL,
[SettingValueDateTimeOffSet] datetimeoffset NULL,
[SettingValueDateTime2] datetime2 NULL,
[SettingValueSmallDateTime] smalldatetime NULL,
[SettingValueDateTime] datetime NULL,
[SettingValueTime] time NULL,
[SettingValueVarChar] varchar(1024) NULL,
[SettingValueChar] char NULL,
[InsertDate] [datetime] NOT NULL DEFAULT (GETDATE()),
[InsertedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),
[LastUpdated] [datetime] NOT NULL DEFAULT (GETDATE()),
[LastUpdatedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),
)
Run Code Online (Sandbox Code Playgroud)
现在,如果这太多了,并且您决定对所有值使用“字符串”,那么这里是一些 DDL。
DROP TABLE [dbo].[SystemSetting]
DROP TABLE [dbo].[SystemSettingCategory]
CREATE TABLE [dbo].[SystemSettingCategory] (
[SystemSettingCategoryId] [int] NOT NULL,
[SystemSettingCategoryName] [nvarchar](64) NOT NULL,
[InsertDate] [datetime] NOT NULL DEFAULT (GETDATE()),
[InsertedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),
[LastUpdated] [datetime] NOT NULL DEFAULT (GETDATE()),
[LastUpdatedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),
CONSTRAINT [PK_SystemSettingCategory] PRIMARY KEY CLUSTERED ([SystemSettingCategoryId] ASC),
CONSTRAINT UQ_SystemSettingCategoryName UNIQUE NONCLUSTERED ([SystemSettingCategoryName])
)
CREATE TABLE [dbo].[SystemSetting] (
[SystemSettingId] [int] NOT NULL,
[SystemSettingCategoryId] INT NOT NULL, /* FK to [SystemSettingCategory], not shown here */
[SettingKeyName] [nvarchar](64) NOT NULL,
[SettingValue] nvarchar(1024) NULL,
[InsertDate] [datetime] NOT NULL DEFAULT (GETDATE()),
[InsertedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),
[LastUpdated] [datetime] NOT NULL DEFAULT (GETDATE()),
[LastUpdatedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),
CONSTRAINT [PK_SystemSetting] PRIMARY KEY CLUSTERED ([SystemSettingId] ASC),
CONSTRAINT FK_SystemSettingCategory_SystemSettingCategoryId foreign key ([SystemSettingCategoryId]) references [SystemSettingCategory] ([SystemSettingCategoryId]),
CONSTRAINT UQ_SystemSettingCategoryId_SettingKeyName UNIQUE NONCLUSTERED ( [SystemSettingCategoryId] , [SettingKeyName] )
)
INSERT INTO [dbo].[SystemSettingCategory] ( [SystemSettingCategoryId] , [SystemSettingCategoryName] )
select 101 , 'EmployeeSettings' UNION ALL select 201, 'StopLightSettings'
INSERT INTO [dbo].[SystemSetting] ( [SystemSettingId] , [SystemSettingCategoryId] , [SettingKeyName] , [SettingValue] )
select 1001 , 101 , 'MininumAgeRequirementMonths' , convert(varchar(16) , (12 * 18))
UNION ALL select 1002 , 101 , 'MininumExperienceMonths' , convert(varchar(8) , 24)
UNION ALL select 2001 , 201 , 'RedLightPosition' , 'top'
UNION ALL select 2002 , 201 , 'YellowLightPosition' , 'middle'
UNION ALL select 2003 , 201 , 'GreenLightPosition' , 'bottom'
/* should fail */
/* start
INSERT INTO [dbo].[SystemSettingCategory] ( [SystemSettingCategoryId] , [SystemSettingCategoryName] )
select 3333 , 'EmployeeSettings'
INSERT INTO [dbo].[SystemSettingCategory] ( [SystemSettingCategoryId] , [SystemSettingCategoryName] )
select 101 , 'xxxxxxxxxxxxxx'
INSERT INTO [dbo].[SystemSetting] ( [SystemSettingId] , [SystemSettingCategoryId] , [SettingKeyName] , [SettingValue] )
select 5555 , 101 , 'MininumAgeRequirementMonths' , 555
INSERT INTO [dbo].[SystemSetting] ( [SystemSettingId] , [SystemSettingCategoryId] , [SettingKeyName] , [SettingValue] )
select 1001 , 101 , 'yyyyyyyyyyyyyy' , 777
INSERT INTO [dbo].[SystemSetting] ( [SystemSettingId] , [SystemSettingCategoryId] , [SettingKeyName] , [SettingValue] )
select 5555 , 555 , 'Bad FK' , 555
end */
Select * from [dbo].[SystemSetting] where [SystemSettingCategoryId] = 101 /* employee related */
Select * from [dbo].[SystemSetting] where [SystemSettingCategoryId] = 201 /* StopLightSettings related */
Run Code Online (Sandbox Code Playgroud)
现在,更进一步,您仍然可以创建具有正确数据类型的强类型 dotnet 对象,然后将数据读取器/数据集转换为强对象,如下所示。
public class EmployeeSettings
{
public Int16 MininumAgeRequirementMonths { get; set; }
public Int16 MininumExperienceMonths{ get; set; }
}
public class StopLightSettings
{
public string RedLightPosition { get; set; }
public string YellowLightPosition { get; set; }
public string GreenLightPosition { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
您仍然可以使用 C# 类(或任何语言)........并使用上面的 SettingDataType 方法。“映射”代码只需要一些额外的工作。
当没有被否决时,我使用 SettingDataType 和 C# 类,如上所示。
我认为2列(名称,值)设计要好得多。如您所说,如果您需要添加新的属性,那么您要做的就是添加insert
新行。在其他设计(单行)中,您需要更改表架构以为新属性添加一列。
但是,这取决于您的属性列表将来是否会更改。
归档时间: |
|
查看次数: |
25446 次 |
最近记录: |