154

私の要件は次のとおりです。

  • 任意のデータ型のユーザー定義フィールドを動的に追加できる必要があります
  • UDFをすばやくクエリできる必要があります
  • データ型に基づいてUDFで計算できる必要があります
  • データ型に基づいてUDFをソートできる必要があります

その他の情報:

  • 主にパフォーマンスを探しています
  • UDFデータを添付できるマスターレコードは数百万あります
  • 私が最後にチェックしたとき、現在のデータベースには5,000万を超えるUDFレコードがありました
  • ほとんどの場合、UDFは数千のマスターレコードにのみ添付され、すべてではありません
  • UDFは結合されておらず、キーとして使用されていません。これらは、クエリまたはレポートに使用される単なるデータです

オプション:

  1. StringValue1、StringValue2 ... IntValue1、IntValue2、...などで大きなテーブルを作成します。私はこのアイデアが嫌いですが、他のアイデアよりも優れていると誰かが教えてくれれば、それを検討します。

  2. 必要に応じてオンデマンドで新しい列を追加する動的テーブルを作成します。また、すべての列にインデックスを付けないとパフォーマンスが低下するので、このアイデアは好きではありません。

  3. UDFName、UDFDataType、およびValueを含む単一のテーブルを作成します。新しいUDFが追加されたら、そのデータだけをプルして、指定されたタイプに解析するビューを生成します。解析基準を満たさないアイテムはNULLを返します。

  4. データ型ごとに1つずつ、複数のUDFテーブルを作成します。したがって、UDFStrings、UDFDatesなどのテーブルがあります。おそらく#2と同じことを行い、新しいフィールドが追加されるたびにビューを自動生成します。

  5. XMLデータ型?私はこれまでこれらを扱ったことがありませんが、言及されているのを見たことがあります。特にパフォーマンスに関して、私が望む結果が得られるかどうかはわかりません。

  6. 他に何かありますか?

4

14 に答える 14

52

パフォーマンスが主な関心事である場合、私は#6 ... UDFごとのテーブルを使用します(実際、これは#2のバリアントです)。この回答は、この状況と、説明されているデータ分散およびアクセスパターンの説明に合わせて特別に調整されています。

長所:

  1. 一部のUDFにはデータセット全体のごく一部の値があることを示しているため、別のテーブルを使用すると、UDFをサポートするために必要なだけのサイズになるため、最高のパフォーマンスが得られます。関連するインデックスについても同じことが言えます。

  2. また、集計やその他の変換のために処理する必要のあるデータの量を制限することで、速度を向上させることができます。データを複数のテーブルに分割すると、UDFデータに対して集計やその他の統計分析を実行し、その結果を外部キーを介してマスターテーブルに結合して、集計されていない属性を取得できます。

  3. データが実際に何であるかを反映するテーブル/列名を使用できます。

  4. データ型の使用、制約の確認、デフォルト値などを完全に制御して、データドメインを定義できます。オンザフライのデータ型変換によるパフォーマンスの低下を過小評価しないでください。このような制約は、RDBMSクエリオプティマイザがより効果的な計画を作成するのにも役立ちます。

  5. 外部キーを使用する必要がある場合、組み込みの宣言型参照整合性が、トリガーベースまたはアプリケーションレベルの制約の適用によってパフォーマンスが向上することはめったにありません。

短所:

  1. これにより、多くのテーブルが作成される可能性があります。スキーマの分離や命名規則を適用すると、これが軽減されます。

  2. UDFの定義と管理を操作するために必要なアプリケーションコードは他にもあります。これは、元のオプション1、3、および4よりも必要なコードがまだ少ないと思います。

その他の考慮事項:

  1. UDFをグループ化するのに意味のあるデータの性質について何かがある場合は、それを奨励する必要があります。このようにして、これらのデータ要素を1つのテーブルに組み合わせることができます。たとえば、色、サイズ、コストのUDFがあるとします。データの傾向は、このデータのほとんどのインスタンスが次のように見えることです。

     'red', 'large', 45.03 
    

    それよりも

     NULL, 'medium', NULL
    

    このような場合、1つのテーブルに3つの列を組み合わせても、顕著な速度の低下は発生しません。これは、NULLとなる値が少なく、3つの列すべてにアクセスする必要がある場合に必要な結合が2つ少なくなるため、テーブルを2つ増やす必要がないためです。 。

  2. 人口が多く頻繁に使用されるUDFからパフォーマンスの壁にぶつかった場合は、それをマスターテーブルに含めることを検討する必要があります。

  3. 論理テーブルの設計は特定のポイントに到達する可能性がありますが、レコード数が非常に多くなる場合は、選択したRDBMSによって提供されるテーブルパーティションオプションも確認する必要があります。

于 2011-03-01T06:46:57.570 に答える
23

私はこの問題についてたくさん書いています。最も一般的な解決策は、エンティティ-属性-値のアンチパターンです。これは、オプション#3で説明したものと似ています。 疫病のようなこのデザインは避けてください

本当に動的なカスタムフィールドが必要なときにこのソリューションに使用するのは、それらをXMLのブロブに格納することです。これにより、いつでも新しいフィールドを追加できます。ただし、スピーディーにするために、検索または並べ替える必要のあるフィールドごとに追加のテーブルも作成します(フィールドごとのテーブルではなく、検索可能なフィールドごとのテーブルのみ)。これは、転置インデックス設計と呼ばれることもあります。

このソリューションに関する2009年の興味深い記事は、http: //backchannel.org/blog/friendfeed-schemaless-mysqlで読むことができます。

または、ドキュメント指向のデータベースを使用することもできます。この場合、ドキュメントごとにカスタムフィールドがあることが期待されます。Solrを選択します。

于 2011-03-03T07:59:05.930 に答える
12

これは、MongoDBやCouchDBなどの非リレーショナルソリューションによってより適切に解決される可能性のある問題のように聞こえます。

どちらも動的なスキーマ拡張を可能にすると同時に、求めるタプルの整合性を維持できます。

私はビル・カーウィンに同意します。EAVモデルはあなたにとってパフォーマンスの高いアプローチではありません。リレーショナルシステムで名前と値のペアを使用することは本質的に悪いことではありませんが、名前と値のペアが情報の完全なタプルを作成する場合にのみうまく機能します。これを使用すると、実行時にテーブルを動的に再構築する必要があり、あらゆる種類のものが困難になり始めます。クエリはピボットメンテナンスの演習になるか、タプルの再構築をオブジェクトレイヤーにプッシュするように強制します。

オブジェクトレイヤーにスキーマルールを埋め込まない限り、nullまたは欠落している値が有効なエントリであるかエントリの欠如であるかを判断することはできません。

スキーマを効率的に管理する機能が失われます。100文字のvarcharは、「値」フィールドの正しいタイプですか?200文字?代わりにnvarcharにする必要がありますか?これは難しいトレードオフになる可能性があり、セットの動的な性質に人為的な制限を課す必要があります。「ユーザー定義フィールドはx個までしか持つことができず、それぞれの長さはy文字のみにすることができます。

MongoDBやCouchDBのようなドキュメント指向のソリューションでは、ユーザーに関連付けられたすべての属性を単一のタプル内で維持します。結合は問題ではないので、誇大広告にもかかわらず、これら2つのどちらも結合でうまくいかないので、人生は幸せです。ユーザーは、約4MBに達するまで管理が難しくならない長さで、必要な数の属性を定義できます(または許可します)。

ACIDレベルの整合性を必要とするデータがある場合は、ソリューションを分割して、高整合性データをリレーショナルデータベースに配置し、動的データを非リレーショナルストアに配置することを検討してください。

于 2011-03-06T08:24:13.283 に答える
11

私はおそらく次の構造のテーブルを作成します:

  • varchar名
  • varcharタイプ
  • 10進数のNumberValue
  • varchar StringValue
  • 日付DateValue

もちろん、正確なタイプはニーズによって異なります(もちろん、使用しているDBMSによっても異なります)。intおよびbooleansにNumberValue(decimal)フィールドを使用することもできます。他のタイプも必要になる場合があります。

値を所有するマスターレコードへのリンクが必要です。マスターテーブルごとにユーザーフィールドテーブルを作成し、単純な外部キーを追加するのがおそらく最も簡単で最速です。このようにして、ユーザ項目でマスタレコードを簡単かつ迅速にフィルタリングすることができます。

ある種のメタデータ情報が必要になる場合があります。したがって、次のようになります。

テーブルUdfMetaData

  • int id
  • varchar名
  • varcharタイプ

テーブルMasterUdfValues

  • int Master_FK
  • int MetaData_FK
  • 10進数のNumberValue
  • varchar StringValue
  • 日付DateValue

何をするにしても、テーブル構造を動的に変更することはありません。メンテナンスの悪夢です。また、XML構造は使用しません。速度が遅すぎます。

于 2011-03-01T18:05:45.683 に答える
6

カスタム列を追加するユーザーを提供する場合でも、それらの列に対するクエリが適切に実行されるとは限りません。クエリの設計には多くの側面があり、それらを適切に実行できます。その中で最も重要なのは、最初に何を格納するかについての適切な仕様です。したがって、基本的に、ユーザーが仕様を考慮せずにスキーマを作成し、そのスキーマから情報をすばやく取得できるようにする必要がありますか?もしそうなら、特にユーザーがデータの数値分析を行えるようにしたい場合、そのようなソリューションがうまくスケーリングするのは不幸です。

オプション1

IMOこのアプローチは、スキーマが何を意味するのかについての知識がなくてもスキーマを提供します。これは、災害のレシピであり、レポート設計者にとっては悪夢です。つまり、どの列にどのデータが格納されているかを知るには、メタデータが必要です。そのメタデータが台無しになると、データを無駄にする可能性があります。さらに、間違ったデータを間違った列に簡単に入れることができます。(「なに?String1には修道院の名前が含まれていますか?それはChalie Sheenのお気に入りの薬だと思いました。」)

オプション3,4,5

IMO、要件2、3、および4は、EAVの変動を排除します。このデータのクエリ、並べ替え、計算が必要な場合、EAVはクトゥルフの夢であり、開発チームとDBAの悪夢です。EAVはパフォーマンスの点でボトルネックを作成し、必要な情報にすばやく到達するために必要なデータの整合性を提供しません。クエリはすぐにクロス集計のゴーディアンノットに変わります。

オプション2,6

それは本当に1つの選択肢を残します:仕様を収集してからスキーマを構築します。

クライアントが保存したいデータで最高のパフォーマンスを望む場合は、開発者と協力してニーズを理解し、可能な限り効率的に保存されるようにする必要があります。それでも、テーブルのスキーマに基づいてフォームを動的に構築するコードを使用して、残りのテーブルとは別のテーブルに格納できます。列の拡張プロパティを許可するデータベースがある場合は、それらを使用して、フォームビルダーが適切なラベルやツールチップなどを使用できるようにすることもできます。これにより、必要なのはスキーマを追加することだけです。いずれにせよ、レポートを効率的に作成して実行するには、データを適切に保存する必要があります。問題のデータに多くのnullが含まれる場合、一部のデータベースにはそのタイプの情報を格納する機能があります。例えば、

これが、分析、フィルタリング、または並べ替えが行われないデータのバッグにすぎない場合は、EAVのいくつかのバリエーションでうまくいく可能性があります。ただし、要件を考えると、これらの新しい列を別々のテーブルに格納し、それらのテーブルから動的にフォームを作成する場合でも、適切な仕様を取得することが最も効率的な解決策になります。

スパース列

于 2011-03-04T23:50:39.770 に答える
5
  1. データ型ごとに1つずつ、複数のUDFテーブルを作成します。したがって、UDFStrings、UDFDatesなどのテーブルがあります。おそらく#2と同じことを行い、新しいフィールドが追加されるたびにビューを自動生成します。

私の調査によると、データ型に基づく複数のテーブルはパフォーマンスに役立たないでしょう。特に、50以上のUDFを持つ20Kまたは25Kレコードなどのバルクデータがある場合。パフォーマンスは最悪でした。

次のような複数の列を持つ単一のテーブルを使用する必要があります。

varchar Name
varchar Type
decimal NumberValue
varchar StringValue
date DateValue
于 2015-09-11T11:32:49.940 に答える
4

これは問題のある状況であり、どの解決策も「正しく」見えません。ただし、オプション1は、単純さとパフォーマンスの両方の点でおそらく最良です。

これは、一部の営利企業アプリケーションで使用されるソリューションでもあります。

編集

現在利用可能であるが、質問が最初に尋ねられたときに存在しなかった(または少なくとも成熟していなかった)別のオプションは、DBでjsonフィールドを使用することです。

多くのリレーショナルDBは、jsonベースのフィールド(サブフィールドの動的リストを含めることができます)をサポートし、それらのフィールドでクエリを実行できるようになりました。

postgress

mysql

于 2011-02-24T15:08:43.123 に答える
3

私たちのデータベースは、ユーザーが7kを超える「カスタムフィールド」を持つSaaSアプリ(ヘルプデスクソフトウェア)を強化します。組み合わせたアプローチを使用します。

  1. (EntityID, FieldID, Value)データを検索するためのテーブル
  2. データの表示entitiesに使用される、すべてのエンティティ値を保持するテーブル内のJSONフィールド。(この方法では、値の値を取得するために100万のJOINは必要ありません)。

この回答が示唆するように、#1をさらに分割して「データ型ごとのテーブル」を作成することもできます。これにより、UDFにインデックスを付けることもできます。

PS「エンティティ-属性-値」アプローチを擁護するためのいくつかの言葉は、誰もがバッシングを続けています。私たちは何十年もの間#2なしで#1を使用してきましたが、それは問題なく機能しました。時にはそれはビジネス上の決定です。アプリを書き直してデータベースを再設計する時間はありますか、それとも最近は本当に安いクラウドサーバーに数ドルを投じることができますか?ちなみに、#1のアプローチを使用していたとき、DBは数百万のエンティティを保持し、数十万のユーザーがアクセスし、16GBのデュアルコアデータベースサーバーは問題なく動作していました。

于 2017-12-22T21:09:26.740 に答える
2

私は経験または1、3、4を経験しましたが、データが何であるかが明確でないか、データを動的なタイプのレコードに分解するためのある種のソフト分類で実際に複雑になるため、すべてが乱雑になります。

XMLを試してみたいと思いますが、xmlの内容に対してスキーマを適用して、UDFデータの異なるセットを保持するのに役立つデータの入力などをチェックできるはずです。新しいバージョンのSQLサーバーでは、XMLフィールドにインデックスを付けることができます。これは、パフォーマンスに役立つはずです。(http://blogs.technet.com/b/josebda/archive/2009/03/23/sql-server-2008-xml-indexing.aspxを参照)たとえば

于 2011-02-24T15:09:36.293 に答える
2

SQL Serverを使用している場合は、sqlvariantタイプを見落とさないでください。それはかなり速く、あなたの仕事をするはずです。他のデータベースにも同様のものがある可能性があります。

XMLデータ型は、パフォーマンス上の理由からあまり良くありません。サーバーで計算を行う場合は、これらを常に逆シリアル化する必要があります。

オプション1は音が悪く、見た目が悪いですが、パフォーマンスの面で最善の策です。パフォーマンスに勝るものがないため、以前にField00〜Field99という名前の列を持つテーブルを作成しました。INSERTのパフォーマンスも考慮する必要がある場合があります。その場合は、これも考慮に入れる必要があります。見栄えを良くしたい場合は、いつでもこのテーブルにビューを作成できます。

于 2011-02-24T15:13:06.257 に答える
1

SharePointはオプション1を使用し、妥当なパフォーマンスを発揮します。

于 2011-02-24T15:10:37.973 に答える
1

私は過去にこれらのオプションのどれも使用せずにこれを非常にうまく管理しました(オプション6?:))。

ユーザーが操作できるモデルを作成し(xmlとして保存し、カスタムモデリングツールを介して公開します)、モデルで生成されたテーブルとビューから、ベーステーブルをユーザー定義のデータテーブルに結合します。したがって、各タイプには、コアデータを含むベーステーブルと、ユーザー定義フィールドを含むユーザーテーブルがあります。

例としてドキュメントを取り上げます。一般的なフィールドは、名前、タイプ、日付、作成者などです。これはコアテーブルに含まれます。次に、ユーザーは、contract_end_date、renewal_clause、blah blah blahなど、独自のフィールドを使用して独自の特別なドキュメントタイプを定義します。そのユーザー定義ドキュメントの場合、共通の主キーで結合されたコアドキュメントテーブルであるxcontractテーブルがあります(したがって、xcontractsの主キーもコアテーブルの主キーで外部になります)。次に、これら2つのテーブルをラップするビューを生成します。クエリ時のパフォーマンスは高速でした。追加のビジネスルールをビューに埋め込むこともできます。これは私にとって本当にうまくいきました。

于 2011-03-04T09:53:49.150 に答える
0

コメントの中で、UDFフィールドは、ユーザーによって適切にマップされていないインポートされたデータをダンプするためのものであるとおっしゃっていました。

おそらく別のオプションは、各ユーザーが作成したUDFの数を追跡し、6つ(または他の同様にランダムな制限)のカスタムフィールドトップを使用できると言って、フィールドを再利用するように強制することです。

このようなデータベース構造の問題に直面した場合、アプリケーションの基本設計(この場合はインポートシステム)に戻って、さらにいくつかの制限を加えるのが最善の場合がよくあります。

今私がすることは、ユーザーへのリンクを追加したオプション4(編集)です。

general_data_table
id
...


udfs_linked_table
id
general_data_id
udf_id


udfs_table
id
name
type
owner_id --> Use this to filter for the current user and limit their UDFs
string_link_id --> link table for string fields
int_link_id
type_link_id

次に、パフォーマンスを最適化し、インデックスを正しく取得するためのビューを作成してください。このレベルの正規化により、DBフットプリントは小さくなりますが、アプリケーションはより複雑になります。

于 2011-03-07T09:55:16.557 に答える
0

このタイプのシステムは、高度に認定されたeコマースCMSプラットフォームであるMagentoで使用されていたため、 #4をお勧めします。単一のテーブルを使用して、fieldIdおよびlabel列を使用してカスタムフィールドを定義します。次に、データ型ごとに個別のテーブルを用意し、それらの各テーブル内に、fieldIdとデータ型のの列でインデックスを付けるインデックスを作成します。次に、クエリで次のようなものを使用します。

SELECT *
FROM FieldValues_Text
WHERE fieldId IN (
    SELECT fieldId FROM Fields WHERE userId=@userId
)
AND value LIKE '%' + @search + '%'

これにより、私の意見では、ユーザー定義型に対して可能な限り最高のパフォーマンスが保証されます。

私の経験では、月に数百万のユーザーにサービスを提供し、カスタム製品属性を持つ数千の製品をホストし、データベースがレポート用であってもワークロードを簡単に処理するいくつかのMagentoWebサイトで作業しました。

レポートの場合、 FieldsテーブルのラベルPIVOT値を列名に変換してから、クエリ結果を各データ型テーブルからそれらのピボットされた列にピボットするために使用できます。

于 2019-03-22T21:11:22.917 に答える