web-dev-qa-db-ja.com

カスタムフィールドを使用してユーザーデータベースをどのように設計しますか

この質問は、データベースをどのように設計すればよいかに関するものです。それは、より良い解決策に応じて、リレーショナル/ nosqlデータベースになる可能性があります


「会社」と「ユーザー」を追跡するためのデータベースを必要とするシステムを作成する必要があるという要件が与えられています。 1人のユーザーは常に1つの会社にのみ属しています

  • ユーザーは1つの会社にしか所属できません
  • 企業は多くのユーザーを持つことができます

「会社」テーブルの設計は非常に簡単です。会社には次の属性/列があります:(シンプルにしましょう)

ID, COMPANY_NAME, CREATED_ON

最初のシナリオ

シンプルでわかりやすい、ユーザーはすべて同じ属性を持っているので、これはリレーショナルスタイルのユーザーテーブルで簡単に実行できます。

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

2番目のシナリオ

異なる会社がユーザーの異なるプロファイル属性を保存したい場合はどうなりますか?各会社には、その会社のすべてのユーザーに適用される定義済みの属性セットがあります。

例えば:

  • A社が保管したいもの:LIKE_MOVIE(ブール)、LIKE_MUSIC(ブール)
  • B社は保存したい:FAV_CUISINE(String)
  • C社は保存したい:OWN_DOG(ブール値)、DOG_COUNT(整数)

アプローチ1

力ずくの方法は、ユーザーに単一のスキーマを持たせ、会社に属していない場合はnullを許可することです。

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, LIKE_MOVIE, LIKE_MUSIC, FAV_CUISINE, OWN_DOG, DOG_COUNT, CREATED_ON

これは、多くのNULLSとそれらに関係のない列を持つユーザー行になるため、やや厄介です(つまり、A社に属するすべてのユーザーはFAV_CUISINE、OWN_DOG、DOG_COUNTにNULL値を持っています)。

アプローチ2

2番目のアプローチは、「自由形式フィールド」を持つことです。

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_1, CUSTOM_2, CUSTOM_3, CREATED_ON

カスタムフィールドが何であるかわからないので、それ自体は厄介です。データ型は、格納された値を反映しません(たとえば、int値をVARCHARとして格納します)。

アプローチ3

私はPostgreSQL JSONフィールドを調べましたが、その場合は次のようになります。

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_PROFILE_JSON, CREATED_ON

この場合、ユーザーに異なるスキーマをどのように適用できますか? A社のユーザーには、次のようなスキーマがあります。

 {"LIKE_MOVIE":"boolean", "LIKE_MUSIC": "boolean"}

C社のユーザーのスキーマは異なりますが、

 {"OWN_DOG ":"boolean", "DOG_COUNT": "int"}

この問題をどのように解決すればよいですか?どのようにしてデータベースを適切に設計して、単一の「オブジェクト」(ユーザー)が持つ関係(会社)に基づいてこの柔軟なスキーマを可能にすることができますか?

関係ソリューション? nosqlソリューション?


編集:ユーザー属性を列ではなく行に格納する「CUSTOM_PROFILE」テーブルについても考えました。

このアプローチには2つの問題があります。

1)データはユーザーごとに増加します列ではなく行として増加します-これは、ユーザーの全体像を取得するには、多くの結合で行われ、異なるカスタム属性の「カスタムプロファイル」テーブルへの複数の結合

2)データが整数またはブール値であることがわかっている場合でも、データ値は常にVARCHARとして格納されて汎用になります。

21
noobcser

代替案としてこれを考慮してください。前の2つの例ではどちらも、アプリケーションのスコープが拡大するにつれてスキーマを変更する必要があります。また、「custom_column」ソリューションの拡張と維持は困難です。最終的には、Custom_510になり、このテーブルがどれほどひどいものになるか想像してみてください。

まず、Companyスキーマを使用します。

[Companies] ComnpanyId, COMPANY_NAME, CREATED_ON

次に、すべての会社で使用/共有される最上位の必須属性にユーザースキーマも使用します。

[Users] UserId, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

次に、各企業のカスタムユーザー属性に固有の動的属性を定義するテーブルを作成します。したがって、ここでは、Attribute列の値の例は "LikeMusic"になります。

[UserAttributeDefinition] UserAttributeDefinitionId, CompanyId, Attribute

次に、ユーザー属性値を保持するUserAttributesテーブルを定義します

[UserAttributes] UserAttributeDefinitionId, UserId, Value

これは、パフォーマンスを向上させるためにさまざまな方法で変更できます。 UserAttributesに複数のテーブルを使用して、それぞれをValueに格納されるデータ型に固有にすることも、VarCharのままにして、キー値ストアとして使用することもできます。

また、CompanyIdをUserAttributeDefinitonテーブルから相互参照テーブルに移動して、将来のプルーフを行うこともできます。

14
P. Roe

NoSQLデータベースを使用します。会社とユーザーのドキュメントがあります。ユーザーは、ユーザーテンプレートに基づいてスキーマの一部を動的に作成します(その会社のフィールド/タイプを示すテキスト)。

\Company\<uniqueidentifier>
    - Name: <Name>
    - CreatedOn: <datetime>
    - UserTemplate: <Text>

\User\<uniqueidentifier>
    - COMPANY_ID: <ID>
    - FIRST_NAME: <Text>
    - LAST_NAME: <Text>
    - EMAIL: <Text>
    - CREATED_ON: <datetime>
    - * Dynamically created fields per company

これは Firebase.com のように表示される場合があります。選択した方法でそれを行う方法を学ぶ必要があります。

7
JeffO

カスタムフィールドリクエストが頻繁に発生する場合は、データベースと同じようにモデル化します。各カスタムフィールド、CompanyCustomField(所属するユーザー、データ型など)に関するメタデータを保持するテーブルと、CustomerId、FieldId、および値を含む別のテーブルCompanyCustomFieldValuesを作成します。 Microsoft SQL Serverのようなものを使用している場合、値の列はsql_variantデータ型になります。

もちろん、管理者が各顧客のカスタムフィールドを定義できるインターフェイスと、このメタデータを実際に使用してフィールド値を収集するUIを構築する別のインターフェイスが必要になるため、これは簡単ではありません。また、フィールドをグループ化したり、選択リストのようなフィールドを実行する必要があるなど、他の要件がある場合は、メタデータや他のテーブル(CompanyCustomFieldPickListOptionsなど)を追加する必要があります。

これは簡単なことではありませんが、新しいカスタムフィールドごとにデータベースの変更やコードの変更を必要としないという利点があります。カスタムフィールドの他の機能もコード化する必要があります(たとえば、正規表現で文字列値を検証する場合、特定の範囲間の日付のみを許可する場合、または別のカスタムフィールド値に基づいて1つのカスタムフィールドを有効にする必要がある場合)。 )。

3
Andy

他の回答の代わりに、profile_attribなどのテーブルを用意するか、スキーマをアプリケーションで完全に管理することもできます。

カスタム属性が追加されると、あなたはALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1)を削除することを禁止できます。これにより、結合を最小限に抑えながら、柔軟性を提供します。

ビットトレードオフは、アプリケーションがデータベースに対するテーブルの変更権限を必要とするようになり、列名のサニタイズについて賢明でなければならないことだと思います。

1
Chris Seufert

あなたの質問には多くの潜在的な解決策があります。 1つの解決策は、追加の属性をXMLとして保存することです。 XMLは、テキストとして、またはXMLとしてXMLタイプをサポートするデータベース(SQL Server)を使用している場合に格納できます。テキストとして保存すると、クエリ機能(カスタム属性の検索など)が制限されますが、保存と取得だけで十分な場合は、これが適切なソリューションです。クエリを実行する必要がある場合は、XMLをXMLタイプとして保存することをお勧めします(ただし、これはベンダー固有です)。

これにより、顧客テーブルに追加列を追加するだけで、顧客に任意の数の属性を格納できるようになります。属性をハッシュセットまたはディクショナリとして保存できます。最初はすべて文字列であるため、タイプセーフが失われますが、日付、数値、ブール値に標準形式の文字列を適用すると、問題なく機能します。

詳細については:

https://msdn.Microsoft.com/en-us/library/hh403385.aspx

@WalterMittyの答えも有効ですが、異なる属性を持つ多くの顧客がいる場合、継承モデルに従うと多くのテーブルが作成される可能性があります。顧客間で共有されるカスタム属性の数によって異なります。

0
Jon Raynor