web-dev-qa-db-ja.com

アプリケーション構成またはアプリケーションオプション設定に最適なテーブル設計?

一連の構成値をデータベースに保存する必要があります。それらを格納するために考えたいくつかの方法は、2つの列(名前、値)と各ペアの行を持つテーブル、または各構成パラメーターの列と1つの行を持つテーブルですか?最初の行では、構成値を追加するために別の行を追加するだけでよく、2番目の行では、テーブルに列を追加する必要があります。考慮に入れるべき問題はありますか? 1つは他よりも効率的ですか?

45
Andrew

構成データについては、構成エントリごとに1行のキー/値構造を使用します。このデータを一度読み取ってキャッシュする可能性が高いため、パフォーマンスは問題になりません。ご指摘のとおり、構成キーのセットが変更されるたびに列を追加するには、さらに多くのメンテナンスが必要です。

SQLは、類似した(同じではないにしても)構造化データの任意の大規模なセットのモデリングと操作に優れています。構成情報のセットは実際にはそうではありません-単一行のデータを取得していますOR完全に無関係なデータの複数行を取得しています。つまり、これを使用しているだけです。データストアとして、SQLデータモデルをスキップして単純化します。

26
Peter Cardona

検討すべき最初の問題はこれです。情報を取得する効率について考えるのをやめます。何よりもまず、データをモデル化する方法効果的に正しくを理解し、次に(そしてそのときのみ)データを作成する方法効果的にを理解します。

したがって、保存する構成データの性質によって異なります。個別の(名前、値)のペアが基本的に無関係である場合は、行ごとに1つとして保存します。それらが関連している場合は、複数の列を持つスキームを検討することをお勧めします。

関連とはどういう意味ですか?いくつかのキャッシュ構成を検討してください。各キャッシュにはいくつかの属性があります。

  • 立ち退きポリシー;
  • 有効期限;
  • 最大サイズ。

各キャッシュに名前があると仮定します。このデータは3つの行として保存できます。

  • <name>_EVICTION
  • <name>_EXPIRY
  • <name>_MAX_SIZE

しかし、このデータは関連であり、多くの場合、一度にすべてを取得する必要があります。その場合、5つの列(id、name、eviction、expiry、max_size)を持つcache_configテーブルを作成することは理にかなっています。

それが関連データで私が意味することです。

14
cletus

もう1つの考慮事項:各構成パラメーターの列を使用すると、バージョンを簡単に設定できます。各行はバージョンを表します。

14
Luc M

各(アプリケーション)構成設定(またはアプリケーションオプション)に個別の行を使用することの1つの欠点は、適切なデータ型の列に設定値を格納できないことです。ユーザーは無効なtypeでデータを入力できますか?それはあなたのアプリケーションに関係がありますか?

個別の列を使用する利点の1つは、DB自体のコード(たとえば、ストアドプロシージャ、関数など)が適切なデータ型の値を使用できることです。最初に無効な値を確認してから適切なデータ型に変換する必要はありません。 。

変更をアプリケーションDBに手動で展開している場合、EAV設計を使用している場合はそうですvery新しい構成設定を展開する方が少し簡単ですが、実際には次のような節約になります。

INSERT Options ( ConfigurationSetting, Value )
VALUES ( 'NewConfigurationSetting', NewConfigurationSettingValue )

対:

ALTER TABLE Options ADD NewConfigurationSetting some_datatype

UPDATE Options
SET NewConfigurationSetting = NewConfigurationSettingValue
13
Kenny Evitt

2列(名前、値)のデザインの方がはるかに優れていると思います。既に述べたように、新しいプロパティを追加する必要がある場合は、新しい行を "insert"するだけです。他の設計(単一行)では、テーブルスキーマを変更して、新しいプロパティの列を追加する必要があります。

ただし、これは、プロパティのリストが将来変更されるかどうかによって異なります。

5
Aziz

ここで私は AppSettingsをデータベーステーブルにいつ移動したかについてブログを書きます。 パフォーマンスは問題ではありません。アプリケーションと辞書に格納され、簡単に検索できます。

アプリケーションについてはわかりませんが、これを行った重要な理由は、DevやTestなどの場合、Production値を使用できないことです。

4
JBrooks

文字列以外の値を文字列列に配置する(別名、不正データ型)ことを重視しています。 (@Kenny Evittが上記で説明したように)

だから私は以下のalternativeを思いつき、正しいデータ型を扱います。

私は実際にはお金と小さなお金を使いません。しかし、私は完全を期すためにそれらを含めました。他にもいくつかのデータ型があります。

見る

https://msdn.Microsoft.com/en-us/library/ms187752.aspx?f=255&MSPPError=-2147217396

しかし、以下はほとんどのものをカバーしています。

正直なところ、文字列(varchar(1024))、int、smallint、およびbit ... 99%のみを使用しています。

それは完璧ではありません。別名、nullタプルがたくさんあります。ただし、これらを1回(およびキャッシュ)取得するだけなので、(私の世界では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()),    

さて、それが多すぎて、すべての値に「文字列」を使用すると決めた場合、ここにいくつかの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 */

さて、さらに大きくして、正しいデータ型で強く型付けされたドットネットオブジェクトを作成し、次に示すようにデータリーダー/データセットを強いオブジェクトに変換できます。

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; }
}

C#クラス(または任意の言語)を実行できます。そして、上記のSettingDataTypeメソッドを使用します。 「マッピング」コードは少し余分な作業を必要とします。

投票されていない場合は、上記のように、SettingDataTypeクラスとC#クラスを使用します。

3
granadaCoder

XMLを使用すると、構成を効率的に保存できます。一部のデータベースは、値をxmlデータ型として保存し、その特定の列でXQUERYを実行できるPure XML機能をサポートしています。

2つの列名と構成を持つテーブルを作成します。文字列データ型の名前とxmlデータ型の構成なので、新しい構成パラメーターの挿入と削除について心配する必要はありません。xmlに新しいタグを追加するだけです。また、データベースがXMLをサポートしていない場合は、それを文字列として保存するだけで、XML形式で保存することで、その構成を手動で解析したり、APIを効率的に使用したりできます。

これは完全な設定を文字列として保存するよりも良い方法だと思います。

2
GG.
CREATE TABLE Configuration (
    Name ...,
    Value ...,
);

最良の方法。通常、テーブルに列を追加するのは面倒です。1行のテーブルのポイントは何ですか。

これがSQLに適しているかどうかはわかりませんが、残念ながら質問に回答しました。

1
Jed Smith

「最良」は完全にコンテキストに依存します-このデータはどのように使用されますか?

必要なのは、構成設定の1つのセットを保存および取得することだけであれば、そもそもリレーショナルデータベースの使用に疑問を投げかけます。ファイルシステムの構成ファイルに比べて明らかな利点はありません。構成ファイルのバージョン管理を簡単に使用することはできません。環境の違い(「DEV」、「TEST」、「PRODUCTION」環境など)を管理するには、データベースを変更するためのGUIが必要になります(ああ、どのようにそもそもデータベース?).

アプリケーションが構成全体について「理由」を示す必要がある場合-たとえば、マルチテナントソリューションがあり、現在のシステムに基づいてアプリケーションを動的に構成する必要がある場合-構成ファイルをテキストドキュメントとしてデータベースに保存し、アプリケーションがドキュメントを保存/取得できるようにするメタデータを含めることをお勧めします。データベースエンジンが異なれば、テキストドキュメントを格納するためのソリューションも異なります。たとえば、マルチテナンシーシステムでは次のようになります。

ID client_id valid_from     valid_until configuration_file
-------------------------------------------------------
1         1   2016/03/16         NULL      <<DOCUMENT>>

これにより、3月3日以降有効だったクライアント1のファイルを取得し、アプリケーションが必要とするすべてのことを実行できます。

アプリケーション自体がエンティティーとしての構成ではなく、構成の内容について推論する必要がある場合は、別の問題があります。提案する「名前/値」ソリューションは、エンティティ/属性/値(EAV)とも呼ばれ、 lotsof[〜#〜]がありますso [〜#〜]questions 利点と欠点について議論します。 TL; DR:EAVを使用する場合、単純な質問でさえSQLに変換するのは困難です。

各構成設定が適切なデータ型の列である場合、データをクエリする方がはるかに簡単です。しかし、これは非常に「幅の広い」テーブル(ほとんどのアプリケーションには数十または数百の構成値がある)になり、構成設定を追加するたびにデータベーススキーマが変更されることを意味します。実用的です。

代わりに、構成値を構造化ドキュメントとして保存することもできます。XMLとJSONが広くサポートされています。これらの形式は、データベースエンジンによって照会できますが、固定スキーマは必要ありません。

1
Neville Kuyt

私は両方の方法を使用しており、2カラムの方法を好みます。各構成の新しい列の欠点は、新しい設定を追加するためにコードを変更する必要があることです。

設定方法ごとに1つの列を使用することを好みます(値にアクセスする場合)。これは、構成設定がより明示的に設定されているためです。ただし、その設定は、テーブルに新しい構成を追加する難しさを補うものではありません。

2カラム方式をお勧めします。次に、アクセス関数/ sprocを設定して値を取得します。

0
Vaccano

依存します。

15未満の値がある場合は、それぞれに1つの列を作成します。

設定の数を定期的に変更する場合、またはすべての設定を頻繁に使用しない場合は、設定ごとに行を作成することを検討します。

それを超えて、それはおそらくトサップです。使用パターンによって異なります。常にすべての設定を取得する必要がある場合は、おそらく1行に設定するのが最も早いでしょう。

列を追加することはそれほど難しくはなく、賢明にプログラミングすれば、通常は他のコードを更新する必要はありません。

0
JasonWoof