web-dev-qa-db-ja.com

SQL Serverデータベースで単一行構成テーブルを使用します。悪いアイデア?

ショッピングカートアプリケーションの開発で、管理者の好みと要件に基づいて設定と構成を保存する必要があることがわかりました。この情報には、会社情報、配送アカウントID、Paypal APIキー、通知設定などが含まれます。

リレーショナルデータベースシステムに単一の行を格納するテーブルを作成することは非常に不適切と思われます。

この情報を保存する適切な方法は何ですか?

注:私のDBMSはSQL Server 2008であり、プログラミング層はASP.NET(C#)で実装されています。

139
David Murdoch

過去にこの2つの方法(単一行テーブルとキー/値ペアテーブル)を実行しましたが、それぞれのアプローチにはプラスとマイナスがあります。

単列

  • positive:値は正しいタイプに保存されます
  • ポジティブ:コードでの扱いが簡単です(上記の理由)
  • positive:デフォルト値を各設定に個別に与えることができます
  • 否定:新しい設定を追加するにはスキーマの変更が必要です
  • 負:設定が多い場合、テーブルは非常に広くなる可能性があります

キー/値ペア

  • ポジティブ:新しい設定を追加するためにスキーマを変更する必要はありません
  • positive:テーブルスキーマが狭く、新しい設定に余分な行が使用されています
  • 負:各設定には同じデフォルト値があります(null/empty?)
  • 負の値:すべてを文字列として保存する必要があります(nvarcharなど)
  • ネガティブ:コードで設定を処理する場合、設定がどのタイプであるかを知ってキャストする必要があります

単一行オプションは、作業がはるかに簡単です。これは、各設定をデータベースの正しいタイプに保存でき、コードのルックアップキーと同様に設定のタイプを保存する必要がないためです。

このアプローチを使用して気になっていたことの1つは、「特別な」単一行設定テーブルに複数の行があることです。私はこれを(SQL Serverで)克服しました:

  • デフォルト値が0の新しいビット列を追加する
  • この列の値が0であることを確認するためのチェック制約の作成
  • ビット列に一意の制約を作成する

つまり、ビット列の値は0でなければならないため、テーブルには1行しか存在できませんが、一意の制約のため、その値の行は1行しか存在できません。

180
adrianbanks

(少なくとも)情報タイプと情報値の列を持つテーブルを作成する必要があります。これにより、新しい情報が追加されるたびに新しい列を作成する必要がなくなります。

10
Otávio Décio

1行でも問題なく機能します。強力なタイプもあります。

show_borders    bit
admin_name      varchar(50)
max_users       int

欠点の1つは、スキーマの変更(alter table)新しい設定を追加します。 1つの代替方法は正規化です。この場合、次のようなテーブルになります。

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

これには弱い型(すべてがvarchar)がありますが、新しい設定を追加すると、行を追加するだけで、データベースへの書き込みアクセスだけで実行できます。

6
Andomar

個人的には、それが機能する場合は、1行に保存します。 SQLテーブルに保存するのはやりすぎですか?おそらく、しかしそうすることで実際に害はありません。

4
E.J. Brennan

ご想像のとおり、最も単純な状況を除いて、すべての構成パラメーターを単一の行に配置することには多くの欠点があります。それは悪い考えです...

構成および/またはユーザー設定タイプの情報を保存する便利な方法は、XMLです。多くのDBMSはXMLデータ型をサポートしています。 XML構文を使用すると、この構成が進化するにつれて、構成を記述する「言語」と構造を拡張できます。 XMLの利点の1つは、階層構造を暗黙的にサポートしていることです。たとえば、構成パラメーターの小さなリストを番号付きのサフィックスを付けずに格納できるようになります。 XML形式の潜在的な欠点は、このデータの検索と一般的な変更が他のアプローチほど単純ではないことです(複雑なことはありませんが、単純/自然ではありません)

リレーショナルモデルにより近いままにしたい場合、 Entity-Attribute-Valueモデル はおそらく必要なものであり、個々の値は通常次のような表に格納されます。

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は、可能な各属性(この場合は「構成パラメーター」)が定義されているテーブルへの外部キーです。

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であるか、管理する構成が1つしかない場合は暗黙的です。

EAVモデルは、アプリケーションの進化に応じて可能な構成パラメーターのリストを拡大できるほか、「メタデータ」、つまり属性自体に関連するデータをデータテーブルに配置するため、一般的に見られる列名のすべてのハードコーディングを回避します構成パラメーターが単一の行に保管されている場合。

3
mjv

正規化アプローチで新しい構成パラメーターを追加するときにスキーマを変更する必要はありませんが、おそらく新しい値を処理するためにコードを変更している可能性があります。

展開に「変更テーブル」を追加することは、単一行アプローチの単純さとタイプセーフティのトレードオフのように思えません。

3
Dave Mikesell

キーと値のペアは、構成設定を保存できる.Net App.Configに似ています。

そのため、値を取得する場合は次のようにします。

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

これを行う一般的な方法は、プロパティファイルに類似した「プロパティ」テーブルを持つことです。ここでは、すべてのアプリ定数を保存することも、必要なだけの定数を保存することもできます。

その後、必要に応じてこのテーブルから情報を取得できます。同様に、保存する他の設定があることがわかったら、追加できます。例を次に示します。

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である場合、それがどのような設定であるかがわかります。

いつか、ここにも管理者以外の情報を保存する必要がある場合に、プロパティタイプが役立ちます。

この方法でカートのデータを保存したり、その問題のルックアップを保存したりしないでください。ただし、データが システム 具体的には、この方法を使用できます。

例:を保存する場合 DATABASE_VERSION、このようなテーブルを使用します。そうすれば、アプリをアップグレードする必要があるときに、プロパティテーブルをチェックして、クライアントが使用しているソフトウェアのバージョンを確認できます。

ポイントは、カートに関連するものにこれを使用したくないということです。適切に定義されたリレーショナルテーブルにビジネスロジックを保持します。プロパティテーブルはシステム情報専用です。

1
Stephano

Varcharとしてのキー列とJSONとしての値列があります。 1は数値ですが、"1"は文字列です。 truefalseは両方ともブール値です。オブジェクトを持つこともできます。

0
kzh

メジャータイプごとに列を追加し、データが含まれている列を示す1つの列を追加することにより、変換なしでキー/値ペアを実行できます。

したがって、テーブルは次のようになります。

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, 

それはもう少し部屋を使用しますが、せいぜい数十個の属性を使用しています。 column_num値のcaseステートメントを使用して、正しいフィールドをプル/ジョインできます。

0
spintool

単一の行が構成の最適な実装であるかどうかはわかりません。 2つの列(configName、configValue)を持つ構成アイテムごとに行を作成する方がよい場合がありますが、これにはすべての値を文字列にキャストして戻す必要があります。

とにかく、グローバル設定に単一の行を使用しても害はありません。 DB(グローバル変数)に保存する他のオプションはさらに悪いです。最初の設定行を挿入し、テーブルでの挿入を無効にして複数の行を防ぐことで制御できます。

0
sidereal

申し訳ありませんが、後で来ます。とにかく、私がやることはシンプルで効果的です。私は単純に3つの()列を持つテーブルを作成します。

ID-int(11)

名前-varchar(64)

値-テキスト

新しい構成列を作成、更新、または読み取る前に私がしていることは、「値」をシリアル化することです!このようにして、タイプがわかります(まあ、phpは:))

例えば:

b:0; [〜#〜] b [〜#〜] OOLEAN(false

b:1; [〜#〜] b [〜#〜] OOLEAN(true

i:1988; [〜#〜] i [〜#〜] NT

s:5: "Kader"; [〜#〜] s [〜#〜] 5文字の長さのTRING

これが役立つことを願っています:)

0
Kader Bouyakoub