この問題 (カテゴリごとに異なるプロパティ) に対するベスト プラクティスは何ですか?
-
03-07-2019 - |
質問
いくつかのカテゴリに属する製品がいくつかあります。
各カテゴリは異なるプロパティを持つことができます。
例えば、
- カテゴリー 車 特性を持っています 色、 力、 ...
- カテゴリー ペット 特性を持っている 重さ, 年, ...
カテゴリ数は10~15程度です。各カテゴリの物件数は 3 ~ 15 です。商品数が非常に多いです。
このアプリの主な要件は、優れた検索機能です。カテゴリを選択し、このカテゴリの各プロパティの基準を入力します。
このシナリオ用にデータベースを設計する必要があります。(SQLサーバー2005)
解決
古典的な設計アプローチは次のようになります (星印は主キー列を示します)。
Product
ProductId*
CategoryId: FK to Category.CategroyId
Name
Category
CategoryId*
Name
Property
PropertyId*
Name
Type
CategoryProperty
CategoryId*: FK to Category.CategoryId
PropertyId*: FK to Property.PropertyId
ProductProperty
ProductId*: FK to Product.ProductId
PropertyId*: FK to Property.PropertyId
ValueAsString
すべてのプロパティ値が文字列として DB に送信され、型変換情報がプロパティ テーブルに保存されるという事実を受け入れることができる場合は、このレイアウトで十分です。
クエリは次のようになります。
SELECT
Product.ProductId,
Product.Name AS ProductName,
Category.CategoryId,
Category.Name AS CategoryName,
Property.PropertyId,
Property.Name AS PropertyName,
Property.Type AS PropertyType,
ProductProperty.ValueAsString
FROM
Product
INNER JOIN Category ON Category.CategoryId = Product.CategoryId
INENR JOIN CategoryProperty ON CategoryProperty.CategoryId = Category.CategoryId
INNER JOIN Property ON Property.PropertyId = CategoryProperty.PropertyId
INNER JOIN ProductProperty ON ProductProperty.PropertyId = Property.PropertyId
AND ProductProperty.ProductId = Product.ProductId
WHERE
Product.ProductId = 1
より多くの WHERE 条件を指定するほど (結合的に、例:AND を使用すると)、クエリが高速になります。つまり、テーブルに適切にインデックスを作成した場合です。
現状では、このソリューションは全文インデックス作成の状況には理想的ではありません。ProductId に関連付けられたすべてのテキストをより非正規化された方法で保存する追加のテーブルがここで役立ちます。このテーブルは、ProductProperty テーブルの変更をリッスンするトリガーを通じて更新する必要があります。
他のヒント
アプリケーションのユーザーの場合 もっている 検索する前にカテゴリを選択するには、製品をカテゴリごとに異なるデータベース テーブルに分割します。この解決策は、カテゴリ自体に共通点がほとんどないという事実からもわかります。カテゴリ別に分類すると、ユーザーがペットを探しているときに車の中を検索する無駄な時間がなくなるため、各検索が大幅に高速化されます。
製品をカテゴリに分割したら、各カテゴリの製品の共通プロパティを使用してテーブルを簡単に作成できます。アプリケーションのユーザー インターフェイスは動的である必要があります (私は Web フォームを考えています)。ユーザーがカテゴリを選択すると、ユーザーが選択できるプロパティが変化する必要があります。
複数のカテゴリにリストしたい製品がある場合、この解決策ではテーブル内に重複したデータが作成されることに注意してください。データベースを設計する場合、速度と正規化の間にはトレードオフの関係があります。もし、あんたが しないでください 複数のカテゴリに当てはまる商品がある場合、これが (検索速度の点で) 最も速い解決策になると思います。
ほとんどの人は、Entity-Attribute-Value (EAV) 設計のバリエーションを使用することを推奨しています。この設計はあなたの状況には過剰であり、たとえば次のような多くの問題を引き起こします。
- 属性のデータ型を定義することはできません。整数属性として「バナナ」と入力できます
- 属性を必須として宣言することはできません(つまり、従来のテーブルでは NOT NULL)
- 属性に対して外部キー制約を宣言することはできません
カテゴリの数が少ない場合は、Bogdan Maxim の回答のソリューション A を使用することをお勧めします。つまり、すべてのカテゴリに共通の属性を持つ 1 つのテーブル Products を定義し、カテゴリ固有の属性を格納するためにカテゴリごとに 1 つの追加テーブルを定義します。
カテゴリの数が無限にある場合、または Products の行ごとに異なる属性セットをサポートする必要がある場合にのみ、EAV が適切なソリューションとなります。ただし、EAV は正規化のいくつかのルールに違反するため、リレーショナル データベースをまったく使用していないことになります。
本当にそこまでの柔軟性が必要な場合は、データを XML で保存することをお勧めします。実際、RDF やセマンティック Web フレームワークを検討してみるとよいでしょう。 ゴマ.
検討してみてはいかがでしょうか エンティティの属性値 任意の名前と値の属性ペアを各製品に「タグ付け」できる配置のタイプです。
これを試すことができます。あなたの質問の実際の詳細はあまりわかりませんが、誰かがもう少しうまく翻訳できるよう手伝ってくれるかもしれません。
5テーブル。3 はデータの保存用、2 はデータ間のマッピングの保存用です。
tProduct
productID
<other product details>
tCategory
categoryID
<other category details>
tProperty
propertyID
<other property details>
tProductXCategory
productyID
categoryID
tCategoryXProperty
categoryID
propertyID
クエリではマッピング テーブルを使用してデータを結合する必要がありますが、これにより、カテゴリ、プロパティ、製品の間にさまざまな多対多の関係を持たせることができます。
検索のパフォーマンスを向上させるには、ストアド プロシージャまたはパラメータ化されたクエリを使用します。
カテゴリとプロパティを柔軟に設定したい場合は、次のテーブルを作成する必要があります。
- 製品:製品番号
- カテゴリー:カテゴリID、プロダクトID
- 財産:プロパティID、カテゴリID
複数の製品でカテゴリを共有したい場合は、n:m 結合のリンク テーブルを作成する必要があります。
- 製品カテゴリポインタ:ProdCatID、ProductID、CategoryID。
クエリでいくつかの結合を行う必要がありますが、適切なインデックスを使用すれば、データを高速にクエリできるはずです。
もっとオブジェクト指向的なものを試してみることもできます。
1.Products のベース テーブルを定義する
Products(ProductID, CategoryID, <any other common properties>)
2.テーブルのカテゴリを定義する
Categories(CategoryID, Name, Description, ..)
ここからは多くのオプションがあり、そのほとんどすべてがデータベースの正規化を破壊します。
解決策 A.
新しい製品を追加する必要がある場合、メンテナンスは悪夢のような作業になります
A1.カテゴリごとに個別のテーブルを定義する
Cars(CarID, ProductID, ..)
Pets(PetID, ProductID, ..)
A2.データを使用するには、リレーションシップに基づいてテーブルを結合します。
SELECT <fields> FROM Cars INNER JOIN Products ON Cars.ProductID = Products.ProductID
解決策 B.
さまざまな種類の物件(つまり、int、varchar など)
B1.プロパティのテーブルを定義する
CategoryProperty (CPID, Name, Type)
B2.カテゴリとプロパティ間の関連付けを保持するテーブルを定義します。
PropertyAssociation (CPID, PropertyID)
B12.プロパティを保持するテーブルを定義します (B1 および B2 の代替)
Properties(CategoryID, PropertyID, Name, Type)
B3.プロパティのタイプ (int、double、varchar など) ごとに値テーブルを追加します。
PropertyValueInt(ProductID, CPID, PropertyID, Value)
- intの場合PropertyValueString(ProductID, CPID, PropertyID, Value)
- 文字列の場合PropertyValueMoney(ProductID, CPID, PropertyID, Value)
- お金のために
B4.すべてのテーブルを結合して、目的のプロパティを取得します。
このアプローチを使用すると、すべてのプロパティを別のテーブルで管理する必要がなく、プロパティの値の型を管理する必要がなくなります。基本的に、関係するすべてのテーブルはルックアップ テーブルになります。欠点は、各値を取得するには、すべての値の型に対して「Case」を実行する必要があることです。
これらの記事を念頭に置いてください (ここ そして ここ) これらのアプローチのいずれかを選択する場合。 このフォーラムの投稿 これも興味深いものであり、ローカリゼーションに関するものですが、何らかの形で主題に関連しています。
を使用することもできます トマラックの答え 必要に応じて、強力な型指定を追加します。
最近これを行う必要があり、3つのエンティティがあるNHibernateを使用しています
製品カテゴリ オプション オプションカテゴリ
製品には 1* カテゴリーがあります
製品には 1* オプションがあります
オプションには 1 つの OptionCategory があります
これを設定すると、Nhibernate キャッシュを使用できるようになります
乾杯