web-dev-qa-db-ja.com

高度なEAVモデルの効率的なクエリ

次のEAV構造のデータを返す効率的なクエリを作成する方法にちょっとこだわっています。

現在、4つの固定フィールドを含む製品テーブルがすでに存在しています。システムをアップグレードして、各製品の製造元が定義した追加の製品フィールドを無制限に許可したいと考えています。これらの追加フィールドを「パラメーター」と呼びます。

パラメータは、次のデータ型にすることができます。

  • テキスト
  • 日付(範囲)
  • ブール
  • ドロップダウン値(単一)
  • ドロップダウン値(複数)

これに基づいて、次のデータベースモデルを作成しました。

Database Scheme

  • Product:固定フィールドを含む元の製品テーブル。
  • パラメータ:このテーブルは、すべてのタイプのパラメータを定義します。現在5:テキスト、日付、ブール値、ドロップダウン値(単一)、ドロップダウン値(複数)
  • ManufacturerParameter:このテーブルには、製品の製造元(写真に含まれていない製造元テーブル)によって彼のすべての製品について定義されているパラメーターが格納されます。
  • ProductParameter:これは、定義されたすべてのパラメーターの実際の製品データが格納される場所です。タイプ「テキスト」のパラメーターは、その情報をフィールド「テキスト」、「日付」を「DatumBegin」、「DatumEnd」、ビット/ブールを「ブール」に格納します。
  • ParameterValueListItem:このテーブルには、単一結合と複数結合の両方のDropdownlist値の事前定義値が含まれています。これらは、商品を作成または変更するときにドロップダウンリストに表示される値です。
  • ProductParameterValueListItem:Dropdownlist値の実際の製品データが格納される場所です。

ASP.NET C#でWebページを構築する必要があります。これには、定義された各パラメーターとその製品の値を含む、すべての製品データのリストが表示されます。任意のフィールド/パラメーターを検索してリストをフィルターできるようにする必要があります。このリストは、どのフィールド/パラメーターでもソート可能である必要があります。

Product-ProductParameter-ProductParametervalueListItem-ParameterValueListItemを外部結合してリストをフィルタリングし、すべてのデータを含むテーブル結果を取得してから、動的なwhere句を使用してそのリストをフィルタリングし、Webアプリによって作成およびパススルーされました。これは1つのパラメーターで検索すると機能しますが、複数のパラメーターで検索を開始すると、各パラメーターがテーブル結果の異なる行であり、パラメーターAとBの間に「AND」一致がないため、誤った結果が得られます。同じレコード(行)に存在しない。

誰かがこれをどのように達成できるか私にアドバイスできますか?最も完璧なソリューションは、すべてのデータと各パラメーターが同じ製品行の列として表示されている1つのテーブル結果です。これはデータベースレベルで処理するには複雑すぎますか?

ここまで読んでいただきありがとうございます。どんなアドバイスでも大歓迎です。

7
Zeep

まず第一に、設計しようとしているのはおそらく非常に悪い考えです。より良い解決策は、新しいテーブルを追加し、アプリケーションにそれらのテーブルのクエリ方法を理解させる動的スキーマを用意することです(それらをスキーマに配置できます)。これにより、このモデルで発生する可能性のあるすべてのロックおよびクエリプランの問題が大幅に回避されます。 CREATE TABLEを繰り返し実行するアプリケーションに問題はありません。

第二に、Parameterを独自のテーブルに正規化した理由が理解できませんか?それをManufacturerParameterテーブルに直接入れてみませんか。

第3に、現在のモデルの続行を主張する場合、(少なくとも私が要件を正しく解釈している場合は)希望どおりの方法を実現する方法があります。何ができるかは、一致がある場合に検索引数を合計するようにクエリを記述し、HAVINGを使用して一致する値を除外することです。フィールドTextBooleanDatumなどの1つだけがProductParameterレコードごとに入力されると想定しています(おそらく、これを制約付きで強制したいでしょう) )

たとえば、1つのパラメーターがbolean = trueで、他のパラメーターがtext = 'abc'であるすべての製品を検索するには、次のようにします。

SELECT P.Name
FROM Product P
JOIN ProductParameter PP
WHERE P.ID = Foo
  AND PP.Boolean = 1 OR PP.Text = 'abc'  ... /* For each filter */
GROUP BY P.Name /* And any other things you want out of product */
HAVING COUNT(*) >= [Number of where clauses]

この製品のすべてのパラメーターをリストする必要がある場合は、上記のクエリテンプレートをネストされたクエリとして使用し、ProductParameterに結合して戻すことができます。

上記のクエリは、そのテーブルのさまざまなデータ型の文字列表現を持つProductParameterの計算列を維持することで最適化できます。このようにして、上記のORステートメントは、INリスト(テーブル値パラメーターとして渡す必要があります)として書き直すことができます)。

繰り返しますが、あなたがしていることはおそらく非常に間違っています。それを行う場合は、ほとんどのクエリプランを手動で調整する必要があります。オプティマイザはもう役に立ちません。また、クエリバリアントが多すぎず、プランのキャッシュがいっぱいになることが想定されています。

3
Thomas Kejser

誰かがこれをどのように達成できるか私にアドバイスできますか?最も完璧なソリューションは、すべてのデータと各パラメーターが同じ製品行の列として表示されている1つのテーブル結果です。これはデータベースレベルで処理するには複雑すぎますか?

動的SQLを使用して、EAVを多数の列を持つテーブルにピボットし(列の制限は1024に注意してください)、各製品を1行に配置できます。これは、従来のタイトなインデックス付きテーブルほどパフォーマンスが良いとは思いませんが、結果セットをテーブルに具体化して、それにインデックスを付けることができます。

1
g2server