58

Person、、、がSpecialPersonありUserます。PersonただのSpecialPerson人です。サイトにユーザー名やパスワードはありませんが、記録を保持するためにデータベースに保存されます。ユーザーは、サイトに登録されているユーザー名とパスワードに加えて、すべて同じデータを持っているPerson可能性があります。SpecialPerson


この問題にどのように対処しますか?Person人に共通するすべてのデータを格納し、キーを使用してデータを検索するSpecialPerson(特別な人の場合)およびユーザー(ユーザーの場合)、またはその逆のテーブルがありますか?

4

13 に答える 13

58

Martin Fowler のPatterns of Enterprise Application Architecture をご覧ください。

  • 単一テーブルの継承:

    リレーショナル データベースにマッピングする場合、複数のテーブルで継承構造を処理するときにすぐにマウントできる結合を最小限に抑えようとします。単一テーブルの継承は、継承構造のすべてのクラスのすべてのフィールドを単一のテーブルにマップします。

  • クラス テーブルの継承:

    オブジェクトに明確にマップされ、継承構造のどこにでもリンクできるデータベース構造が必要です。クラス テーブル継承は、継承構造内のクラスごとに 1 つのデータベース テーブルを使用することでこれをサポートします。

  • 具体的なテーブルの継承:

    オブジェクト インスタンスの観点からテーブルを考えると、メモリ内の各オブジェクトを取得し、それを 1 つのデータベース行にマップするのが賢明な方法です。これは、継承階層内の各具象クラスのテーブルがある具象テーブル継承を意味します。

于 2008-09-05T13:19:17.337 に答える
46

オブジェクトの継承をデータベース テーブルにマッピングするには、一般に 3 つの方法があります。

タイプの特別なフィールドを使用して、すべてのオブジェクトのすべてのフィールドを含む 1 つの大きなテーブルを作成できます。これは高速ですが、スペースを浪費しますが、最新のデータベースは空のフィールドを保存しないことでスペースを節約します。また、テーブル内のすべてのユーザーのみを探している場合は、すべてのタイプの人がテーブルに含まれているため、処理が遅くなる可能性があります。すべての or-mapper がこれをサポートしているわけではありません。

基本クラスのフィールドを含むすべてのテーブルを使用して、すべての異なる子クラスに対して異なるテーブルを作成できます。これは、パフォーマンスの観点からは問題ありません。しかし、メンテナンスの観点からではありません。基本クラスが変更されるたびに、すべてのテーブルが変更されます。

あなたが提案したように、クラスごとにテーブルを作成することもできます。このように、すべてのデータを取得するには結合が必要です。そのため、パフォーマンスが低下します。それが最もクリーンなソリューションだと思います。

もちろん、何を使用するかは状況によって異なります。どのソリューションも完璧ではないため、長所と短所を比較検討する必要があります。

于 2008-09-05T12:26:59.693 に答える
7

ここで私が言おうとしているのは、データベース アーキテクトを黙想に陥らせることですが、次のようになります。

データベースビューはインターフェイス定義に相当すると考えてください。テーブルはクラスに相当します。

したがって、あなたの例では、3 つの person クラスすべてが IPerson インターフェイスを実装します。したがって、「User」、「Person」、および「SpecialPerson」のそれぞれに 1 つずつ、合計 3 つのテーブルがあります。

次に、ビュー「PersonView」または3つすべてのテーブルから共通のプロパティ(「インターフェース」で定義)を単一のビューに選択するものを作成します。このビューの 'PersonType' 列を使用して、格納されている人物の実際のタイプを格納します。

したがって、あらゆるタイプの人に対して操作できるクエリを実行する場合は、PersonView ビューをクエリするだけです。

于 2008-09-05T12:29:20.093 に答える
6

ユーザー、人物、および特別な人物がすべて同じ外部キーを持っている場合、テーブルは 1 つになります。User、Person、または Special Person に制限される Type という列を追加します。次に、Type の値に基づいて、他のオプションの列に制約を設定します。

オブジェクト コードの場合、ポリモーフィズムを表す個別のテーブルまたは複数のテーブルを使用しても、大きな違いはありません。ただし、データベースに対して SQL を実行する必要がある場合、ポリモーフィズムが単一のテーブルに取り込まれていると、サブタイプの外部キーが同じであれば、はるかに簡単になります。

于 2008-09-05T12:25:15.533 に答える
5

リレーショナル データベースで継承を処理するための 3 つの基本的な戦略と、正確なニーズに応じて、より複雑な/オーダーメイドの代替手段が多数あります。

  • クラス階層ごとのテーブル。階層全体に対して 1 つのテーブル。
  • サブクラスごとのテーブル。サブクラス化されたテーブル間で 0-1 の関連付けを使用して、サブクラスごとに個別のテーブルが作成されます。
  • 具体的なクラスごとのテーブル。具象クラスごとに 1 つのテーブルが作成されます。

これらのアプローチはそれぞれ、正規化、データ アクセス コード、およびデータ ストレージに関する独自の問題を引き起こしますが、私の個人的な好みは、特定のパフォーマンスまたは構造上の理由がない限り、サブクラスごとにテーブルを使用することです。

于 2008-09-05T12:38:34.160 に答える
5

これは、OP が尋ねようとしていたものではないかもしれませんが、ここに入れるかもしれないと思いました。

私は最近、プロジェクトで db ポリモーフィズムのユニークなケースを経験しました。60 から 120 の可能なクラスがあり、それぞれに 30 から 40 の一意の属性の独自のセットと、すべてのクラスで約 10 から 12 の共通の属性があります。私たちは SQL-XML ルートに進むことに決め、最終的に 1 つのテーブルになりました。何かのようなもの :

PERSON (personid,persontype, name,address, phone, XMLOtherProperties)

すべての一般的なプロパティを列として含み、次に大きな XML プロパティ バッグを含みます。次に、ORM レイヤーは、XMLOtherProperties からのそれぞれのプロパティの読み取り/書き込みを担当しました。少し似ている :

 public string StrangeProperty
{
get { return XMLPropertyBag["StrangeProperty"];}
set { XMLPropertyBag["StrangeProperty"]= value;}
}

(XML 列を XML ドキュメントではなく Hastable としてマッピングすることになりましたが、DAL に最も適したものを使用できます)

デザイン賞を受賞することはありませんが、可能なクラスの数が多い (または不明な) 場合は機能します。また、SQL2005 では、SQL クエリで XPATH を使用して、XML として保存されているプロパティに基づいて行を選択できます。これは、わずかなパフォーマンスの低下にすぎません。

于 2008-09-05T13:12:19.553 に答える
4

ここで「建築の宇宙飛行士」になるリスクがあるので、サブクラスごとに別のテーブルを使用する傾向があります。サブクラス テーブルの主キーも、スーパータイプにリンクする外部キーにします。

このようにする主な理由は、論理的な一貫性が大幅に向上し、その特定のレコードに対して NULL で無意味なフィールドが多数作成されることがなくなるためです。また、この方法を使用すると、設計プロセスを反復するときにサブタイプにフィールドを追加するのがはるかに簡単になります。

これにより、クエリに JOIN を追加することのマイナス面が追加され、パフォーマンスに影響を与える可能性がありますが、ほとんどの場合、最初に理想的な設計を採用し、必要に応じて後で最適化を検討します。最初は「最適な」方法で行ったことが何度かありますが、ほとんどの場合、後で後悔しています。

だから私のデザインは次のようなものになります

PERSON (個人ID、名前、住所、電話番号など)

SPECIALPERSON (personid REFERENCES PERSON(personid)、追加フィールド...)

USER (personid REFERENCES PERSON(personid)、username、encryptedpassword、追加フィールド...)

必要に応じて、後でスーパータイプとサブタイプを集約する VIEW を作成することもできます。

このアプローチの 1 つの欠点は、特定のスーパータイプに関連付けられたサブタイプを頻繁に検索する場合です。頭の中でこれに対する簡単な答えはありません。必要に応じてプログラムで追跡するか、そうでなければ、グローバルクエリを実行して結果をキャッシュすることができます。それは本当にアプリケーションに依存します。

于 2008-09-05T12:41:46.913 に答える
3

Person と Special Person の違いにもよりますが、おそらくこのタスクにはポリモーフィズムは必要ありません。

User テーブルを作成します。これは、User に対する null 許容の外部キー フィールドを持つ Person テーブルです (つまり、Person は User になることができますが、そうである必要はありません)。
次に、追加のフィールドを含む Person テーブルに関連する SpecialPerson テーブルを作成します。特定の Person.ID の SpecialPerson にレコードが存在する場合、その人物は特別な人物です。

于 2008-09-05T12:26:51.473 に答える
2

私たちの会社では、すべてのフィールドを 1 つのテーブルに結合することでポリモーフィズムに対処していますが、最悪の場合、参照整合性を強制できず、モデルを理解するのが非常に困難です。私は確かにそのアプローチに反対することをお勧めします。

サブクラスごとにテーブルを使用し、パフォーマンスの低下も回避しますが、タイプに基づいてその場でクエリを作成することにより、すべてのサブクラス テーブルとの結合を回避できる ORM を使用します。前述の戦略は、単一レコード レベルのプルでは機能しますが、一括更新または選択では回避できません。

于 2012-07-17T23:06:54.370 に答える
1

はい、より多くのタイプが存在する可能性がある場合は、PersonType テーブルと共に TypeID も検討します。ただし、3つしかない場合は、necではないはずです。

于 2008-09-05T12:23:59.310 に答える
0

過去に私はあなたが示唆したとおりにそれを行いました.Personテーブルを一般的なものに使用し、次にSpecialPersonを派生クラスにリンクしました。ただし、Linq2Sql は同じテーブル内のフィールドに違いを示したいため、再考しています。ただし、エンティティ モデルについてはあまり調べていません。他の方法を使用できることは確かです。

于 2008-09-05T12:21:56.553 に答える
-1

個人的には、これらのさまざまなユーザー クラスをすべて 1 つのテーブルに格納します。次に、「タイプ」値を格納するフィールドを作成するか、どのフィールドに入力するかによって、相手のタイプを暗示することができます。たとえば、UserID が NULL の場合、このレコードはユーザー ID ではありません。ユーザー。

1 対 1 または 0 のタイプの結合を使用して他のテーブルにリンクすることもできますが、その場合はすべてのクエリで余分な結合を追加することになります。

最初の方法は、そのルートをたどることにした場合、LINQ-to-SQL でもサポートされます (「階層ごとのテーブル」または「TPH」と呼ばれます)。

于 2008-09-05T12:20:39.443 に答える