web-dev-qa-db-ja.com

ユーザー設定のデータベース設計

ユーザー設定を保存するために使用されるテーブルを設計するときに、次のオプションがある場合、そのどれがベストプラクティスと見なされますか?

(オプション1)

USER_SETTINGS
-Id
-Code (example "Email_LimitMax")
-Value (example "5")
-UserId

(オプション2)

たとえば、通知設定で作成する必要がある設定ごとに、新しいテーブルを作成します。

"USER_ALERT_SETTINGS"
-Id
-UserId
-EmailAdded (i.e true)
-EmailRemoved 
-PasswordChanged
...
...

"USER_EMAIL_SETTINGS"
-Id
-UserId
-EmailLimitMax
....

(オプション3)

"USER"
-Name
...
-ConfigXML
41
001

その他の回答では、さまざまなオプションの長所と短所がうまく説明されています。

オプション1(プロパティバッグ)は、特にプロパティバッグの弱点に対する保護を組み込む場合、ほとんどのアプリケーションにとって最適な全体的なデザインであると思います。

次のERDを参照してください。

Property Bag ERD

上記のERDでは、USER_SETTINGテーブルはOPと非常によく似ています。違いは、varchar Code列とValue列の代わりに、この設計は、許容される設定(コード)を定義するSETTINGテーブルへのFKと、値。 1つのオプションは、任意の種類のユーザー入力を受け取ることができるvarcharフィールドで、もう1つは有効な値のテーブルへのFKです。

SETTINGテーブルには、ユーザー設定をFKで定義するか、制約のないvarchar入力で定義するかを示すフラグもあります。 data_typeSETTINGに追加して、USER_SETTING.unconstrained_valueのエンコード方法と解釈方法をシステムに通知することもできます。必要に応じて、SETTING_GROUPテーブルを追加して、ユーザーメンテナンスのさまざまな設定を整理することもできます。

この設計により、設定内容に関するルールをテーブル駆動できます。これは、便利で柔軟性があり、メンテナンスが簡単でありながら、誰でも自由に使用できるようにします。


EDIT:いくつかの例を含むいくつかの詳細...

上記のERDには、列の詳細(SETTINGの範囲値とALLOWED_SETTING_VALUEの列)が追加されていることに注意してください。

以下は、説明のためのサンプルレコードです。

SETTING:
+----+------------------+-------------+--------------+-----------+-----------+
| id | description      | constrained | data_type    | min_value | max_value |
+----+------------------+-------------+--------------+-----------+-----------+
| 10 | Favourite Colour | true        | alphanumeric | {null}    | {null}    |
| 11 | Item Max Limit   | false       | integer      | 0         | 9001      |
| 12 | Item Min Limit   | false       | integer      | 0         | 9000      |
+----+------------------+-------------+--------------+-----------+-----------+

ALLOWED_SETTING_VALUE:
+-----+------------+--------------+-----------+
| id  | setting_id | item_value   | caption   |
+-----+------------+--------------+-----------+
| 123 | 10         | #0000FF      | Blue      |
| 124 | 10         | #FFFF00      | Yellow    |
| 125 | 10         | #FF00FF      | Pink      |
+-----+------------+--------------+-----------+

USER_SETTING:
+------+---------+------------+--------------------------+---------------------+
| id   | user_id | setting_id | allowed_setting_value_id | unconstrained_value |
+------+---------+------------+--------------------------+---------------------+
| 5678 | 234     | 10         | 124                      | {null}              |
| 7890 | 234     | 11         | {null}                   | 100                 |
| 8901 | 234     | 12         | {null}                   | 1                   |
+------+---------+------------+--------------------------+---------------------+

これらの表から、決定できるユーザー設定の一部は、お気に入りの色、アイテムの最大制限、およびアイテムの最小制限であることがわかります。好きな色は、英数字の選択リストです。アイテムの最小値と最大値は、許容範囲の値が設定された数値です。 SETTING.constrained列は、ユーザーが関連するALLOWED_SETTING_VALUEsから選択しているかどうか、またはUSER_SETTING.unconstrained_valueを入力する必要があるかどうかを決定します。ユーザーが設定を操作できるGUIは、提供するオプションと、SETTING.data_typemin_valuemax_valueの制限(存在する場合)を強制する方法を理解する必要があります。

この設計を使用して、ユーザーが選択(または入力)した値に対して基本的な制約/健全性チェックを実施するのに十分なメタデータを含む許容設定をテーブル駆動できます。

編集:クエリの例

以下は、上記のデータを使用して特定のユーザーIDの設定値をリストするSQLの例です。

-- DDL and sample data population...
CREATE TABLE SETTING
    (`id` int, `description` varchar(16)
     , `constrained` varchar(5), `data_type` varchar(12)
     , `min_value` varchar(6) NULL , `max_value` varchar(6) NULL)
;

INSERT INTO SETTING
    (`id`, `description`, `constrained`, `data_type`, `min_value`, `max_value`)
VALUES
    (10, 'Favourite Colour', 'true', 'alphanumeric', NULL, NULL),
    (11, 'Item Max Limit', 'false', 'integer', '0', '9001'),
    (12, 'Item Min Limit', 'false', 'integer', '0', '9000')
;

CREATE TABLE ALLOWED_SETTING_VALUE
    (`id` int, `setting_id` int, `item_value` varchar(7)
     , `caption` varchar(6))
;

INSERT INTO ALLOWED_SETTING_VALUE
    (`id`, `setting_id`, `item_value`, `caption`)
VALUES
    (123, 10, '#0000FF', 'Blue'),
    (124, 10, '#FFFF00', 'Yellow'),
    (125, 10, '#FF00FF', 'Pink')
;

CREATE TABLE USER_SETTING
    (`id` int, `user_id` int, `setting_id` int
     , `allowed_setting_value_id` varchar(6) NULL
     , `unconstrained_value` varchar(6) NULL)
;

INSERT INTO USER_SETTING
    (`id`, `user_id`, `setting_id`, `allowed_setting_value_id`, `unconstrained_value`)
VALUES
    (5678, 234, 10, '124', NULL),
    (7890, 234, 11, NULL, '100'),
    (8901, 234, 12, NULL, '1')
;

そして今度はユーザーの設定を抽出するためのDML:

-- Show settings for a given user
select
  US.user_id 
, S1.description 
, S1.data_type 
, case when S1.constrained = 'true'
  then AV.item_value
  else US.unconstrained_value
  end value
, AV.caption
from USER_SETTING US
  inner join SETTING S1
    on US.setting_id = S1.id 
  left outer join ALLOWED_SETTING_VALUE AV
    on US.allowed_setting_value_id = AV.id
where US.user_id = 234

SQL Fiddle でこれを参照してください。

76
Joel Brown

この単純な例を考えてみましょう。

2つのテーブルがある場合、serTable(ユーザーの詳細が含まれています)およびSettingsTable(設定の詳細が含まれています)。次に、以下に示すように、UserTableとSettingsTableを関連付けるための新しいテーブルserSettingsを作成します。

user settings data base design

この例から正しい解決策が見つかることを願っています。

8
Sajith

オプション1(前述の「プロパティバッグ」)は簡単に実装できます-事前分析はほとんどありません。しかし、それには多くの欠点があります。

  1. UserSettings.Codeの有効な値を制限する場合は、有効なタグのリスト用の補助テーブルが必要です。したがって、(a)UserSettings.Codeの検証がない-アプリケーションコードは任意の値をダンプできるため、バグをキャッチする機会を逃します。または、有効なタグの新しいリストにメンテナンスを追加する必要があります。

  2. UserSettings.Valueはおそらく、それに入る可能性のあるすべての異なる値に対応するために文字列データ型を持っています。そのため、整数、ブール、浮動小数点などの真のデータ型、およびRDMBSが誤った値を挿入したときに実行されるデータ型チェックを失っています。ここでも、潜在的なQA問題を自分で購入しました。文字列値であっても、列の長さを制限する機能を失っています。

  3. コードに基づいて列にDEFAULT値を定義することはできません。したがって、EmailLimitMaxのデフォルトを5にしたい場合は、それを実行できません。

  4. 同様に、[値]列にCHECK制約を設定して無効な値を防ぐことはできません。

  5. プロパティバッグアプローチは、SQLコードの検証を失います。名前付き列アプローチでは、「UserID = xであるUserSettingsからBlahを選択する」というクエリは、Blahが存在しない場合にSQLエラーを取得します。 SELECTがストアドプロシージャまたはビューにある場合、コードが本番環境に移行する前に、プロシージャ/ビューを適用するとエラーが発生します。プロパティバッグアプローチでは、NULLを取得するだけです。そのため、データベースによって提供される別の自動QA機能を​​失い、未検出のバグが発生する可能性があります。

  6. 前述のように、条件が複数のタグに適用されるUserIDを検索するクエリは作成が難しくなります。テストされる条件ごとにテーブルへの結合が1つ必要です。

  7. 残念ながら、プロパティバッグは、アプリケーション開発者が新しいコードをプロパティバッグに貼り付けることを勧めています。他のアプリケーションでの使用方法を分析する必要はありません。大規模なアプリケーションの場合、これは正式にモデル化されていないため、「非表示」プロパティのソースになります。これは、名前付き属性の代わりに純粋なタグ値を使用してオブジェクトモデルを実行するようなものです。これは、エスケープバルブを提供しますが、厳密に型指定された名前付き属性に対してコンパイラーが提供するすべての助けがありません。または、スキーマ検証なしでプロダクションXMLを行うようなものです。

  8. 列名アプローチは自己文書化です。テーブルの列のリストは、開発者に可能なユーザー設定を教えます。

私はプロパティバッグを使用しました。しかし、脱出バルブとしてだけで、私はしばしばそれを後悔しました。 「明示的な列をプロパティバッグにしたかったのに」

8
Tom Wilson

各オプションには場所があり、選択は特定の状況に依存します。以下の各オプションの長所と短所を比較しています。

オプション1:長所:

  • 多くのオプションを処理できます
  • 新しいオプションを簡単に追加できます
  • オプションを管理するための汎用インターフェースを開発できます

オプション1:短所

  • 新しいオプションが追加されると、すべてのユーザーアカウントを新しいオプションで更新するのがより複雑になります。
  • オプション名が暴走することがある
  • 許可されたオプション値の検証はより複雑であり、そのためには追加のメタデータが必要です

オプション2:長所

  • 各オプションは個別の列であるため、各オプションの検証はオプション1よりも簡単です。

オプション2:短所

  • 新しいオプションごとにデータベースの更新が必要です
  • 多くのオプションを使用すると、データベーステーブルが使用しにくくなる可能性があります

実行するクエリの種類に依存するため、「最高」を評価することは困難です。

オプション1(一般に「プロパティバッグ」、「名前と値のペア」、「エンティティ属性値」、またはEAVとして知られています)を使用すると、スキーマが事前にわからないデータを簡単に保存できます。ただし、一般的なリレーショナルクエリを実行することは困難です。たとえば、同等の実行を想像してください

select count(*) 
from USER_ALERT_SETTINGS 
where EmailAdded = 1 
and Email_LimitMax > 5

これは、特にデータベースエンジンがvarcharフィールドを数値的に意味のある方法で比較しない可能性があるため(つまり、「> 5」が期待どおりに機能しない可能性があるため)、非常に複雑になります。

実行するクエリを調べて、それらのクエリを最もよくサポートする設計を確認します。個々のユーザーの制限を確認するだけでよい場合は、プロパティバッグで問題ありません。すべてのユーザーにわたってレポートを作成する必要がある場合は、おそらくそうではありません。

JSONやXMLについても同様です。個々のレコードを保存しても問題ありませんが、すべてのユーザーに対するクエリやレポートが難しくなります。たとえば、メールアドレス「[email protected]」の構成設定を検索するとします。これには、ノード「メールアドレス」を見つけるためにすべてのXMLドキュメントを検索する必要があります。

3
Neville Kuyt