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


18

この質問は、データベースをどのように設計する必要がありますか?それは、より良いソリューションになるものに応じて、リレーショナル/ 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(文字列)
  • 会社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

多くのNULLと、それらに関係のない列を持つユーザー行(つまり、会社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として格納されます


3
異なる企業が各顧客に異なる複数値のデータセットを持っている場合、COMPANY_CUSTOMERリンクテーブルが絶対に必要です。それ以外のものはすぐに大きな痛みを引き起こします。
キリアンフォス

リンクテーブルはカスタムデータにどのように役立ちますか?列は、まだ異なることがあります
noobcser

1
「IKEAのキリアンのパスワードは「子猫」です」という事実を、「COMPANY:IKEA、CUSTOMER:Kilian、ATTRIBUTE:password、VALUE:kitten」などのタプルで表す必要があります。より単純なものは何もしません。
キリアンフォス

3
定義により、スキーマは固定されたものです。必要なフィールドがわからない場合は、設定できません。このような問題がリレーショナルデータベースで解決される傾向がある1つの方法については、Entity-Attribute-Valueご覧ください。
メイソンウィーラー

回答:


13

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

まず、会社のスキーマを使用しましょう。

[Companies] ComnpanyId, COMPANY_NAME, CREATED_ON

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

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

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

[UserAttributeDefinition] UserAttributeDefinitionId, CompanyId, Attribute

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

[UserAttributes] UserAttributeDefinitionId, UserId, Value

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

また、CompanyIdをUserAttributeDefinitonテーブルから将来の校正のために相互参照テーブルに移動することもできます。


おかげで-私はそのようなアプローチについて-編集をご覧ください。2つの問題:1)データは行として大きくなります。つまり、ユーザーの全体像を把握するには、多くの結合を行う必要があります。2)値が実際にintまたはbooleanなどであっても、「value」は常にVARCHARとして格納され、ジェネリックになります
-noobcser

1
テーブルIDにint / bigintを使用し、それらに結合する場合、極端な行になるまでパフォーマンスの問題は発生しません。これで、属性値に基づいて検索を開始した場合、膨大な数のレコードを取得し始めると問題が発生する可能性があります。この場合、私はDBAと協力して、作成できるインデックスがあるかどうか、またはこれらの種類の検索を高速化できるインデックス付きビューがあるかどうかを判断します。私は同様のスキーマを使用しました、それは基本設計がIMOかなりうまく動作しますので、一切のパフォーマンスの問題で年間億のレコードになります
P.卵を

レポート、フィルタリング、クエリが必要な場合、異なる属性が異なるデータセットに属する場合があります。このアプローチはNoSQLよりも優れているでしょうか?パフォーマンスの違いを理解しようとしています。同様の状況では、ユーザーのみがユーザー定義フィールドを含むレポートを定義できます。
コス

上記のアプローチでは、どのように検索物をdiffとして実装しますか。企業は、ユーザーのフィールドも含めて自分のフィールドで検索したいと考えています。これに加えてスケーラブルな検索を提供するための正しいアプローチは何ですか
テクグラマー

多くの結合を使用して通常どおりに検索できます。ETLスクリプトを使用して、検索するデータを抽出し、より非正規化された構造に配置できます。最後に、検索の方法としてインデックス付きビューの利用を試みることができます。個人的には、ETLメソッドを使用して、検索しやすい非正規化構造を生成することをお勧めします。
P.ロー

7

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のようなものでどのように見えるかということです。選択したものでそれを行う方法を学ぶ必要があります。


これが私が考えていること、またはJSON列です。PRoeが提案するソリューションと比較した、クエリ、フィルタリングレポートのパフォーマンスはどうですか。
コス

1
データをjsonまたはxmlに圧縮してから列に投げると、検索が非常に遅くなります。上記の回答で提示されたデータを検索する必要がある場合は、インデックス付きビューを使用してデータを取得することをお勧めします。そのソリューションが理想的でない場合は、ETLを使用して、簡単に検索および報告できる構造にデータをコピーすることをお勧めします。
P.ロー

上記のアプローチでは、どのように検索物をdiffとして実装しますか。企業は、ユーザーのフィールドも含めて自分のフィールドで検索したいと考えています。これに加えてスケーラブルな検索を提供するための正しいアプローチは何ですか
テクグラマー

nosqlデータベースでは、冗長データが存在する場合がありますが、検索可能な方法で構造化されています。上に示したものは一意の識別子によるものです。もう1つは\ Company \ Nameである可能性があります。複数のインデックスを持つことに似ています。
ジェフ

3

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

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

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


おかげで-私はそのようなアプローチについて-編集をご覧ください。2つの問題:1)データは行として大きくなります。つまり、ユーザーの全体像を把握するには、多くの結合を行う必要があります。2)値が実際にintまたはbooleanなどであっても、「value」は常にVARCHARとして格納され、ジェネリックになります
-noobcser

1
@noobcserすべてのデータベースが行と結合を中心に設計した後、行として成長するデータは実際には重要ではありません。いずれにせよ、このようなことを非常に得意とするCommon Table Expressionsを使用する可能性が高くなります。値列のデータ型としてsql_variantを使用できると言った部分を見逃しているかどうかはわかりません。MS SQLサーバーの機能名に名前を付けていますが、他の成熟したDBMSにも同様の機能があると思います。
アンディ

1
@noobcser FYI私は実際に私のキャリアでこれらの要件に頻繁に遭遇し、提案されたソリューションのそれぞれで経験を持っているので、私の経験で最もうまくいったものを提案しています。この種のことでxmlデータ型を使用することが、MSがネイティブデータ型としてxmlを追加することを嫌う理由の一部です。
アンディ

1

他の答えに代わる方法は、profile_attribと呼ばれるテーブル、またはスキーマがアプリケーションによって完全に管理される同様のテーブルを持つことです。

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

ちょっとしたトレードオフは、アプリケーションがデータベースに対してテーブルの変更権限を必要とすることであり、列名のサニタイズについては賢くする必要があると思います。


正規表現[^\w-]+はそれをうまくやるべきであり、0-9A-Za-z_-そうでないものは許可しません。
レギュラージョー

0

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

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

詳細については:

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

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


これでも同様に機能しますが、XML / JSONフィールドに格納されたデータに対して実際に何かを行う必要があると、私は制限されるように感じます。
アンディ

@Andy-確かに、別のレイヤーがあります。単にDBを照会するのではなく、DBを照会し、XMLを解析します。それを制限と呼ぶかどうかはわかりませんが、もっと面倒です。ただし、カスタム属性が広範囲に使用されている場合は、検討する必要があります。
ジョンレイナー

T-SQLでは、ネームスペースに対してXML / JSON列のコンテンツを定義し、カスタムデータの要素に対してクエリを実行できます。難しくありません
スティーブンヨーク

-1

会社プロファイルの異なるタイプごとに3つの異なるテーブルがあるように、データベースを正規化する必要があります。あなたの例を使用すると、列を持つテーブルがあります:

USER_ID, LIKE_MOVIE, LIKE_MUSIC

USER_ID, FAVORITE_CUISINE

USER_ID, OWN_DOG, DOG_COUNT

このアプローチは、企業が事前に保存したい情報の形を知っていて、頻繁に変更されないことを前提としています。設計時にデータの形状が不明な場合は、おそらくそのJSONフィールドまたはnosqlデータベースを使用する方が良いでしょう。


-1

何らかの理由で、データベースは、内部プラットフォーム効果が最も頻繁に現れる1つのフィールドです。これは、アンチパターンがポップアップする別のケースです。

この場合、あなたは自然で正しい解決策と戦おうとしています。会社Aのユーザーは会社Bのユーザーではないため、独自のフィールド用に独自のテーブルが必要です。

データベースベンダーはテーブルごとに料金を請求することはなく、2倍のテーブルに2倍のディスク領域は必要ありません(実際、BのユーザーのAの属性を保存しないため、2つのテーブルを使用する方が効率的です。スペースが必要です)。

もちろん、十分な共通フィールドがある場合は、それらを共有ユーザーテーブルに含めて、会社固有の各ユーザーテーブルに外部キーを持つことができます。これは非常に単純な構造であるため、データベースクエリオプティマイザーがそれに苦労することはありません。必要なJOINは簡単です。


3
数千の顧客がいる場合、各顧客のカスタムフィールドにカスタムコードが必要であることは言うまでもなく、各テーブルがすぐに維持できなくなる可能性があります。
アンディ

@アンディ:何だと思う?数千の異なるスキームを単一のテーブルに混在させると、状況はさらに維持できなくなります!もちろん、カスタムフィールドにはカスタムコードが必要になるでしょう。繰り返しますが、各顧客が個別のきれいなテーブルを持っている場合、それは難しくはありません。X社の分野を他の何千もの会社から選ぶことは血まみれの混乱です。
–MSalters

私の答え、またはすべての余分な列を顧客テーブルに追加するというOPのアイデアを参照していますか?
アンディ

2
ここでの目標は、保守可能でスケーラブルなソリューションを見つけることです。顧客ごとにテーブルを作成することは、間違いなくその反対です。新しい顧客をオンボードするたびに、テーブル作成スクリプトを実行し、コード(エンティティオブジェクト)を更新し、再デプロイするのは現実的ではありません。
tsOverflow

すべての顧客に共有テーブルを使用するというこのアイデア自体は、別個のSaaSアーキテクチャの議論です。顧客を異なるテーブル(または顧客ごとのバックアップ/復元とスケールアウトが可能な異なるデータベース)に保持するのには、いくつかの正当な理由があります。このシナリオでは、メインテーブルにcusotm列を作成するのは簡単です。私は賛成しましたが、なぜ人々はこのアプローチを好まないという理由だけでこれを支持しないのでしょうか。内部プラットフォーム効果は現実です
。EVA

-1

私のソリューションでは、プログラムからこのクエリを呼び出すことを想定しており、後処理を実行できるはずです。次の列を使用できます。

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_VALUES

CUSTOM_VALUESは、キーと値のペアを格納する文字列型です。キーは列名になり、値は列値になります

LIKE_MOVIE;yes;LIKE_MUSIC;no;FAV_CUISINE;rice

このCUSTOM_VALUESには、存在する情報のみが保存されます。プログラムからクエリを実行するときに、この文字列を分割して使用できます。

私はこのロジックを使用してきましたが、問題なく動作します。クエリではなくコードでフィルタリングロジックを適用する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.