回答:
記述するタイプ階層をモデル化するには、少なくとも次の5つのオプションがあります。
単一のテーブル継承:すべての製品タイプの1つのテーブル。すべてのタイプのすべての属性を格納するのに十分な列があります。これは、多くの列を意味し、そのほとんどは特定の行でNULLです。
クラステーブル継承:すべての製品タイプに共通の属性を格納する、製品の1つのテーブル。次に、製品タイプごとに1つのテーブルに、その製品タイプに固有の属性を格納します。
具体的なテーブル継承:一般的な製品属性のテーブルはありません。代わりに、製品タイプごとに1つのテーブルがあり、共通の製品属性と製品固有の属性の両方が格納されています。
シリアル化されたLOB:すべての製品タイプに共通の属性を格納する製品用の1つのテーブル。1つの追加の列には、XML、YAML、JSON、またはその他の形式で、半構造化データのBLOBが格納されます。このBLOBを使用すると、各製品タイプに固有の属性を格納できます。FacadeやMementoなどのデザインパターンを使用してこれを説明できます。ただし、SQL内で簡単にクエリできない属性のblobがある場合でも、ブロブ全体をフェッチしてアプリケーションに戻し、そこで整理する必要があります。
Entity-Attribute-Value:製品用の1つのテーブルと、属性を列ではなく行にピボットする1つのテーブル。EAVは、リレーショナルパラダイムに関して有効な設計ではありませんが、とにかく多くの人々がそれを使用しています。これは、別の回答で言及されている「プロパティパターン」です。いくつかの落とし穴については、StackOverflow のeavタグに関する他の質問を参照してください。
これについてはプレゼンテーションで詳しく書いています Extensible Data Modeling。
EAVに関する追加の考え:多くの人がEAVを支持しているようですが、私はそうではありません。これは最も柔軟なソリューションのようであり、したがって最高です。ただし、格言を覚えておいてください TANSTAAFL。EAVの欠点のいくつかを次に示します。
NOT NULL
)。JOIN
、属性ごとに行う必要があるため、従来の表形式のレイアウトで結果をフェッチすると、複雑でコストがかかります。EAVが提供する柔軟性の程度により、他の領域で犠牲が必要になります。おそらく、コードを従来の方法で元の問題を解決するよりも複雑(またはさらに悪い)にします。
そして、ほとんどの場合、その程度の柔軟性を持つ必要はありません。製品タイプに関するOPの質問では、製品固有の属性について製品タイプごとにテーブルを作成する方がはるかに簡単なので、少なくとも同じ製品タイプのエントリに対していくつかの一貫した構造を適用できます。
すべての行が個別の属性セットを持つ可能性がある場合にのみ、EAVを使用します。製品タイプの有限セットがある場合、EAVは過剰です。クラステーブルの継承は、私の最初の選択肢になります。
2019年の更新:JSONを「多くのカスタム属性」の問題の解決策として使用している人が増えるほど、その解決策が気に入らなくなります。特別なJSON関数を使用してクエリをサポートする場合でも、クエリが複雑になります。通常の行や列に保存する場合と比較して、JSONドキュメントを保存する場合は、はるかに多くのストレージ領域が必要です。
基本的に、これらのソリューションはどれも、リレーショナルデータベースでは簡単でも効率的でもありません。「可変属性」を持つという全体の考えは、根本的に関係理論と対立しています。
結局のところ、アプリにとって最も悪いものに基づいてソリューションの1つを選択する必要があるということです。したがって、データベース設計を選択する前に、データをクエリする方法を知る必要があります。どのソリューションも特定のアプリケーションに最適である可能性があるため、「最良」のソリューションを1つ選択する方法はありません。
@石の心
私はここまでずっとEAVとMVCで行きます。
@ビル・カービン
EAVの欠点のいくつかを次に示します。
- 列を必須にする方法はありません(NOT NULLと同等)。
- SQLデータ型を使用してエントリを検証する方法はありません。
- 属性名のスペルが一貫していることを確認する方法はありません。
- たとえばルックアップテーブルの場合など、特定の属性の値に外部キーを配置する方法はありません。
ここで言及したすべてのこと:
私の意見では、データベースはアプリケーションのプログラミング言語のように適切なレベルでこれらの相互作用と要件を処理することができないため、データベースにはまったく属していません。
私の意見では、データベースをこのように使用することは、岩を使って釘を打ち込むようなものです。あなたは岩を使ってそれを行うことができますが、この種の活動のためにより正確で特別に設計されたハンマーを使用することを想定していませんか?
複数の行から属性を取得するには、属性ごとにJOINを実行する必要があるため、結果をフェッチして従来の表形式のレイアウトは複雑でコストがかかります。
この問題は、部分的なデータに対してクエリをいくつか実行し、アプリケーションでそれらを表形式のレイアウトに処理することで解決できます。600GBの製品データがある場合でも、このテーブルのすべての行のデータが必要な場合は、バッチで処理できます。
さらに進むクエリのパフォーマンスを向上させたい場合は、レポートやグローバルテキスト検索などの特定の操作を選択して、必要なデータを格納し、定期的に再生成されるインデックステーブルを準備できます。たとえば、30分ごとに実行します。
毎日どんどん安くなるので、追加のデータストレージのコストを気にする必要もありません。
アプリケーションによる操作のパフォーマンスが気になる場合は、常にErlang、C ++、Go言語を使用してデータを前処理し、後で最適化されたデータをメインアプリでさらに処理することができます。
you can always use Erlang, C++, Go Language to pre-process the data
どういう意味?DBの代わりにGo langを使用しますか?詳しく説明してもらえますか?
私がClass Table Inheritance
意味を使用する場合:
すべての製品タイプに共通の属性を格納する、製品の1つのテーブル。次に、製品タイプごとに1つのテーブルに、その製品タイプに固有の属性を格納します。-ビル・カーウィン
ビルカーウィンの提案の中で一番気に入っているものはどれですか。問題が発生しないようにする方法を説明するために、1つの欠点を予測できます。
1つのタイプにのみ共通で、次に2、次に3に共通になる属性がある場合、どのような緊急時対応計画を立てる必要がありますか?
例:(これは単なる例であり、私の実際の問題ではありません)
家具を販売する場合、椅子、ランプ、ソファ、テレビなどを販売する場合があります。持ち運びできるのは、消費電力のあるテレビのタイプだけです。したがって、power_consumption
属性をに配置しますtv_type_table
。しかし、それから私たちは、power_consumption
特性も備えたホームシアターシステムを持ち始めます。もう1つの製品だけでいいので、このフィールドをに追加しますstereo_type_table
。これがおそらくこの時点で最も簡単だからです。しかし、時間の経過とともに、ますます多くの電子機器を持ち運ぶようになると、それpower_consumption
が十分に広く、それがにあるべきであることがわかりmain_product_table
ます。私は今どうすればいい?
フィールドをに追加しますmain_product_table
。電子機器をループして、それぞれtype_table
に正しい値を入れるスクリプトを記述しますmain_product_table
。次に、それぞれからその列をドロップしますtype_table
。
ここで、常に同じGetProductData
クラスを使用してデータベースと対話し、製品情報を取得している場合、コードの変更でリファクタリングが必要になった場合は、そのクラスのみを対象にする必要があります。
Productテーブルと、製品ID、追加情報名、追加情報値の3つの列を持つ個別のProductAdditionInfoテーブルを作成できます。すべての種類の製品ではなく多くの種類の製品で色が使用されている場合、それをProductテーブルのnull許容列にするか、単にProductAdditionalInfoに配置することができます。
このアプローチはリレーショナルデータベースの従来の手法ではありませんが、実際に多く使用されているのを見てきました。柔軟性があり、優れたパフォーマンスが得られます。
Steve YeggeはこれをPropertiesパターンと呼び、その使用について長い記事を書いています。
3 columns: product ID, additional info name, additional info value
私は概念を理解しました。そして、私は実際にこれを以前にやったことがあり、問題が発生しました。しかし、今のところ、それらの問題が何であったか思い出せません。