7

これは、私が何年にもわたって複数の場所で見たシナリオです。他の誰かが私よりも優れたソリューションに出くわしたかどうか疑問に思っています...

私の会社は比較的少数の製品を販売していますが、販売する製品は高度に専門化されています (つまり、特定の製品を選択するには、その製品に関するかなりの数の詳細を提供する必要があります)。問題は、特定の製品を選択するために必要な詳細のは比較的一定ですが、必要な詳細の種類は製品によって大きく異なることです。例えば:

製品 X には、(仮説的に) 次のような識別特性がある可能性があります。

  • '色'、
  • '素材'
  • 「平均故障時間」

ただし、製品 Y には特性がある可能性があります

  • '厚さ',
  • '直径'
  • '電源'

製品 X と製品 Y の両方を利用する注文システムを作成する際の問題 (とにかくそのうちの 1 つ) は、ある時点で注文明細が「販売」しているものを参照する必要があることです。製品 X と製品 Y は 2 つの異なるテーブルで定義されているため、幅広いテーブル スキームを使用した製品の非正規化はオプションではありません (製品定義は非常に深い)。注文入力、編集、レポート作成が実用的です。


過去に試したこと

  • 製品 X と製品 Y に共通の列を持つ「製品」という名前の親テーブルを作成し、次に「製品」を OrderLine テーブルの参照として使用し、製品 X のテーブル間のプライマリ サイドとして「製品」との FK リレーションシップを作成します。これは基本的に、'Product' テーブルを OrderLine とすべての異なる製品テーブル (例: Products X と Y) の両方の親として配置します。注文入力には問題なく機能しますが、「製品」レコードは、「製品」をより詳細な子である製品 X または製品に結合する方法を決定するために、製品の種類を追跡する必要があるため、注文のレポートまたは編集で問題が発生します。 Y.利点: キー関係が保持されます。 短所: レポート、注文明細/製品レベルでの編集。
  • 注文明細レベルで「製品タイプ」列と「製品キー」列を作成し、いくつかの CASE ロジックまたはビューを使用して、明細が参照するカスタマイズされた製品を決定します。これは項目 (1) に似ていますが、共通の「製品」テーブルはありません。注文明細行とその製品定義の間の外部キーが完全になくなるため、これはより「迅速かつ汚い」ソリューションだと思います。利点: 迅速な解決。 短所: (1) と同じですが、RI が失われます。
  • 共通のヘッダー テーブルを作成し、カスタマイズされた属性 (OrderLine [n] <- [1] Product [1] <- [n] ProductAttribute) のキーと値のペアを使用して、製品定義を均質化します。 利点: キー関係が保持されます。製品の定義に曖昧さはありません。 短所: レポート (属性を含む製品のリストの取得など)、属性値のデータ入力、パフォーマンス (製品属性の取得、製品属性の挿入または更新など)

他の誰かが別の戦略を試して成功した場合は、ぜひ聞いてみたい.

ありがとうございました。

4

5 に答える 5

5

最初に説明したソリューションは、データの整合性を維持したい場合、および製品タイプが比較的少なく、新しい製品タイプをめったに追加しない場合に最適です。これは、あなたの状況で私が選ぶデザインです。レポートが製品固有の属性を必要とする場合にのみ、レポートは複雑になります。レポートで共通の Products テーブルの属性のみが必要な場合は、問題ありません。

あなたが説明する2番目の解決策は「ポリモーフィックアソシエーション」と呼ばれ、それは良くありません。「外部キー」は実際の外部キーではないため、DRI 制約を使用してデータの整合性を確保することはできません。OO ポリモーフィズムには、リレーショナル モデルに類似点がありません。

属性名を文字列として保存することを含む、あなたが説明する3番目のソリューションは、「Entity-Attribute-Value」と呼ばれる設計であり、これは苦痛で高価なソリューションであることがわかります。データの整合性を確保する方法はなく、1 つの属性を NOT NULL にする方法も、特定の製品に特定の属性セットがあることを確認する方法もありません。ルックアップ テーブルに対して 1 つの属性を制限する方法はありません。多くの種類の集計クエリは SQL では実行できなくなるため、レポートを作成するには多くのアプリケーション コードを作成する必要があります。必要な場合にのみ、EAV 設計を使用します。たとえば、製品タイプの数に制限がなく、属性のリストがすべての行で異なる可能性があり、コードやスキーマを変更せずにスキーマが頻繁に新しい製品タイプに対応する必要がある場合です。

もう 1 つの解決策は、「単一テーブルの継承」です。これは、すべての製品のすべての属性の列を持つ非常に広いテーブルを使用します。特定の行の製品に関係のない列には NULL を残します。これは事実上、属性を NOT NULL として宣言できないことを意味します (すべての製品に共通のグループにある場合を除きます)。また、ほとんどの RDBMS 製品では、1 つのテーブル内の列数、または行全体の幅 (バイト単位) に制限があります。したがって、この方法で表現できる製品タイプの数は限られています。

ハイブリッド ソリューションが存在します。たとえば、共通の属性は通常どおり列に格納できますが、製品固有の属性は Entity-Attribute-Value テーブルに格納できます。または、XML や YAML などの他の構造化された方法で製品固有の属性を Products テーブルの BLOB 列に格納することもできます。しかし、これらのハイブリッド ソリューションは、一部の属性を別の方法でフェッチする必要があるため、問題を抱えています。

このような状況に対する究極のソリューションは、リレーショナル データベースの代わりに RDF を使用して、セマンティック データ モデルを使用することです。これは EAV といくつかの特徴を共有していますが、より野心的です。すべてのメタデータはデータと同じ方法で保存されるため、すべてのオブジェクトは自己記述的であり、データをクエリするのと同じように、特定の製品の属性のリストをクエリできます。JenaSesameなどの特別な製品があり、このデータ モデルと SQL とは異なる特別なクエリ言語を実装しています。

于 2008-09-23T18:26:21.743 に答える
2

あなたが見落とした魔法の弾丸はありません。

「互いに素なサブクラス」と呼ばれることもあります。2つのサブクラス(ProductX)と(ProductY)を持つスーパークラス(Product)があります。これは、リレーショナルデータベースの場合、非常に難しい問題です。[もう1つの難しい問題は、部品表です。もう1つの難しい問題は、ノードとアークのグラフです。]

OrderLineがProductのサブクラスにリンクされているが、どの特定のサブクラスを認識していない(または気にかけていない)ポリモーフィズムが本当に必要です。

モデリングの選択肢はそれほど多くありません。それぞれの悪い機能をほぼ特定しました。これは、ほとんどすべての選択肢です。

  1. すべてをスーパークラスにプッシュします。これは、識別子(type="X"およびtype="Y")と100万列の製品がある単一テーブルのアプローチです。Productの列は、ProductXとProductYの列の和集合です。未使用の列があるため、いたるところにnullがあります。

  2. すべてをサブクラスにプッシュダウンします。この場合、ProductXとProductYの和集合であるビューが必要になります。そのビューは、完全な注文を作成するために結合されたものです。これは最初のソリューションと似ていますが、動的に構築され、適切に最適化されない点が異なります。

  3. スーパークラスインスタンスをサブクラスインスタンスに結合します。この場合、ProductテーブルはProductX列とProductY列の共通部分です。各製品には、ProductXまたはProductYのいずれかのキーへの参照があります。

大胆な新しい方向性は実際にはありません。リレーショナルデータベースの世界観では、これらが選択肢です。

ただし、アプリケーションソフトウェアの構築方法を変更することを選択した場合は、この罠から抜け出すことができます。アプリケーションがオブジェクト指向の場合、ファーストクラスのポリモーフィックオブジェクトですべてを行うことができます。一種の不格好なリレーショナル処理からマップする必要があります。これは2回発生します。1回はデータベースからデータをフェッチしてオブジェクトを作成するとき、もう1回はオブジェクトをデータベースに永続化するときです。

利点は、処理を簡潔かつ正確に記述できることです。オブジェクトとして、サブクラスの関係を持ちます。

欠点は、SQLが単純な一括フェッチ、更新、および挿入に発展することです。

これは、SQLがORMレイヤーに分離され、一種の些細な実装の詳細として管理される場合に利点になります。JavaプログラマーはiBatis(またはHibernate、TopLink、Cocoon)を使用し、PythonプログラマーはSQLAlchemyまたはSQLObjectを使用します。ORMは、データベースのフェッチと保存を行います。アプリケーションは、注文、ライン、および製品を直接操作します。

于 2008-09-23T18:44:05.163 に答える
2

これで始められるかもしれません。もう少し工夫が必要だろう

Table Product ( id PK, name, price, units_per_package)
Table Product_Attribs (id FK ref Product, AttribName, AttribValue)

これにより、属性のリストを製品に添付できます。-- これは基本的に選択肢 3 です。

属性の最大数がわかっている場合は、行くことができます

Table Product (id PK, name, price, units_per_package, attrName_1, attrValue_1 ...)

もちろん、これはデータベースを非正規化しますが、クエリをより簡単にします。

私は最初のオプションを好みます。

  1. 任意の数の属性をサポートします。
  2. 属性名は別のテーブルに保存でき、参照整合性が適用されるため、カナダ人がそこに「色」を貼り付けてレポートを壊すことはありません。
于 2008-09-23T13:37:42.903 に答える
1

製品ラインが変わることはありますか?
もしそうなら、製品ごとにテーブルを作成すると多大な費用がかかりますが、キーと値のペアのアイデアはうまく機能します. それは私が自然に引き寄せられる方向です。

次のようなテーブルを作成します。

Attribute(attribute_id, description, is_listed)    
-- contains values like "colour", "width", "power source", etc. 
-- "is_listed" tells us if we can get a list of valid values: 

AttributeValue(attribute_id, value)
-- lists of valid values for different attributes.  

Product (product_id, description)

ProductAttribute (product_id, attribute_id)  
-- tells us which attributes apply to which products

Order (order_id, etc)

OrderLine (order_id, order_line_id, product_id)

OrderLineProductAttributeValue (order_line_id, attribute_id, value)
-- tells us things like: order line 999 has "colour" of "blue"

これをまとめるための SQL は簡単ではありませんが、それほど複雑でもありません...そして、そのほとんどは (ストアド プロシージャまたはデータ アクセス レイヤーのいずれかで) 一度書き込んで保持します。

さまざまな種類のエンティティで同様のことを行います。

于 2008-09-23T13:44:56.273 に答える
0

Chris と AJ: ご回答ありがとうございます。製品ラインは変更される可能性がありますが、「揮発性」とは言いません。

私が 3 番目のオプションを好まない理由は、製品属性値のメタデータが犠牲になるからです。基本的に列を行に変換し、その過程でデータベース列のほとんどの利点 (データ型、デフォルト値、制約、外部キーの関係など) を失います。

私は実際に、過去にこの方法で製品定義を行ったプロジェクトに参加したことがあります。基本的に、完全な製品/製品属性定義システム (データ型、最小/最大オカレンス、デフォルト値、「必須」フラグ、使用シナリオなど) を作成しました。システムは最終的に機能しましたが、オーバーヘッドとパフォーマンスに大きなコストがかかりました (たとえば、製品を視覚化する具体化されたビュー、製品定義のデータ入力 UI を表示および検証するカスタムの「スマート」コンポーネント、注文ラインで製品インスタンスのカスタマイズ可能な属性を表示する別の「スマート」コンポーネント、blahblahblah)。

繰り返しますが、返信ありがとうございます。

于 2008-09-23T14:01:54.330 に答える