テーブルデザインの経験があまりありません。私の目標は、以下の要件を満たす1つ以上の製品テーブルを作成することです。
多くの種類の製品(TV、電話、PCなど)をサポートします。製品の種類ごとに、次のような異なるパラメーターセットがあります。
電話の色、サイズ、重量、OS ...
PCにはCPU、HDD、RAMが搭載されます...
パラメータのセットは動的である必要があります。任意のパラメーターを追加または編集できます。
製品の種類ごとに個別の表を作成せずにこれらの要件を満たすにはどうすればよいですか?
説明する型階層をモデリングするには、少なくとも次の5つのオプションがあります。
単一テーブル継承 :すべての製品タイプに1つのテーブルがあり、すべてのタイプのすべての属性を格納するのに十分な列があります。これは、多くの列を意味し、そのほとんどは任意の行でNULLです。
クラステーブルの継承 :すべての製品タイプに共通の属性を格納する製品用の1つのテーブル。次に、製品タイプごとに1つのテーブルに、その製品タイプに固有の属性を格納します。
Concrete Table Inheritance :一般的な製品属性のテーブルはありません。代わりに、製品タイプごとに1つのテーブルがあり、共通の製品属性と製品固有の属性の両方が保存されます。
シリアル化LOB :すべての製品タイプに共通の属性を格納する製品用の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は過剰です。クラステーブルの継承が最初の選択肢です。
Update 2019:「多くのカスタム属性」問題の解決策としてJSONを使用している人を見るほど、その解決策が好きではなくなります。特殊な JSON関数 を使用してクエリをサポートしている場合でも、クエリが複雑になります。 JSONドキュメントを保存するには、通常の行と列に保存するよりもはるかに多くの保存スペースが必要です。
基本的に、これらのソリューションはいずれも、リレーショナルデータベースでは簡単でも効率的でもありません。 「可変属性」を持つという考え方全体は、基本的にリレーショナル理論と対立しています。
結局のところ、yourアプリにとって最も悪いソリューションに基づいて、いずれかのソリューションを選択する必要があります。したがって、データベース設計を選択する前に、データのクエリ方法を知る必要があります。どのソリューションも特定のアプリケーションに最適である可能性があるため、「最適な」ソリューションを1つ選択する方法はありません。
@石の心
私はここまでずっとEAVとMVCで行きます。
@ビル・カルビン
EAVの欠点のいくつかを次に示します。
No way to make a column mandatory (equivalent of NOT NULL). No way to use SQL data types to validate entries. No way to ensure that attribute names are spelled consistently. No way to put a foreign key on the values of any given attribute, e.g.
ルックアップテーブル用。
あなたがここで言及したことすべて:
私の意見では、データベースはアプリケーションのプログラミング言語のように適切なレベルでこれらの相互作用と要件を処理することができないため、データベースにはまったく属していません。
私の意見では、この方法でデータベースを使用することは、岩を使用して釘を打つようなものです。あなたは岩でそれを行うことができますが、より正確で、この種の活動のために特別に設計されたハンマーを使用することを想定していませんか?
複数の行から属性を取得するには、各属性に対してJOINを実行する必要があるため、従来の表形式レイアウトでの結果の取得は複雑で費用がかかります。
この問題は、部分的なデータに対してほとんどクエリを実行せず、アプリケーションでテーブルレイアウトに処理することで解決できます。 600GBの製品データがあっても、このテーブルのすべての行のデータが必要な場合、バッチで処理できます。
さらに進むクエリのパフォーマンスを改善したい場合は、たとえばレポート作成またはグローバルテキスト検索を行い、必要なデータを格納し、定期的に再生成されるインデックステーブルを準備します(30分ごとなど)。
毎日のように安くなるため、追加のデータストレージのコストを気にする必要さえありません。
それでもアプリケーションによって実行される操作のパフォーマンスに関心がある場合は、常にErlang、C++、Go Languageを使用してデータを前処理し、後でメインアプリで最適化されたデータをさらに処理することができます。
Class Table Inheritance
の意味を使用する場合:
すべての製品タイプに共通の属性を格納する製品用の1つのテーブル。次に、製品タイプごとに1つのテーブルに、その製品タイプに固有の属性を格納します。 -ビル・カーウィン
私はビル・カーウィンの提案の中で最高のものが好きです。1つの欠点を予見できます。問題になるのを防ぐ方法を説明しようと思います。
1つのタイプにのみ共通する属性が、2、3などに共通になる場合、どのような緊急時対応計画を実施する必要がありますか
例:(これは単なる例であり、実際の問題ではありません)
家具を販売する場合、椅子、ランプ、ソファ、テレビなどを販売する可能性があります。TVタイプは、消費する電力を持ち運ぶ唯一のタイプです。したがって、power_consumption
にtv_type_table
属性を設定します。しかし、その後、power_consumption
プロパティも持つホームシアターシステムの持ち運びを開始します。他の1つの製品だけでOKなので、このフィールドを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はこれを The Propertiesパターン と呼び、それを使用することについての長い投稿を書きました。