web-dev-qa-db-ja.com

関連するテーブルの1つがすでにジャンクションテーブルである多対多の関係があっても問題ありませんか?

データベースを計画するとき、次のような設定になりました。

CompanyProductPersonがあります。

ジャンクションテーブルCompany_Productを介して、CompanyProductの間には多対多の関係があります。これは、特定の会社が複数の製品(「車」など)を生産する場合があるためですと「自転車」)だけでなく、「自動車」などの特定の製品も複数の会社で製造できます。ジャンクションテーブルCompany_Productには、特定の会社が特定の製品を販売する価格である追加のフィールド「price」があります。

ジャンクションテーブルCompany_Productを介して、Company_Product_PersonPersonの間には別の多対多の関係があります。はい。これは、すでにジャンクションテーブルになっている1つのエンティティを含む多対多の関係です。これは、Personがcompany1の車やcompany2の自転車などの複数の製品を所有でき、同じperson_productを複数の人が所有できるためです。たとえば、person1とperson2の両方が車を購入した可能性があるためです。会社1。ジャンクションテーブルCompany_Product_Personには、company_productを購入したときの人の考えが含まれる追加のフィールド「thoughts」があります。

これは一般的に受け入れられるものですか、それとも何か問題があるという兆候ですか?奇妙な場合、より良い解決策は何ですか?

私はこれでいくつかの問題に遭遇してきました( これなど )ので、データベースのセットアップが最初から間違っているのではないかと思っていました。

3
Pedro A

データベースの論理設計を作成する際には、「ジャンクションテーブル」の観点から考えるのではなく、関係の観点から考える必要があります。

最初の「ジャンクションテーブル」は、「Company販売Product for Price "」という関係に対応しているようです。 2番目の「ジャンクションテーブル」は、「Personが所有Product -Companyによって製造された)」という関係に対応しているようです。

完全なコンテキストに依存しますが、2番目のリレーションは、CompanyProduct、およびPersonテーブルへの外部キーを持ち、他の「ジャンクションテーブル」への接続。これにより、会社が製造したことのない製品を個人が所有していると主張するリスクが高まりますが、その製品を偶然所有した人の所有権情報をすべて失うことなく、「販売製品」テーブルから行を削除することもできます。その会社から。多分これはあなたにとって問題ではありません、その場合これはあなたが提案する方法でテーブルを構造化しない理由ではありません、そして私が前に述べたリスクisテーブルを構造化する理由ですあなたが提案する方法で。

より一般的に、私がお勧めするのは(「テーブル」や「ジャンクションテーブル」の観点からではなく、関係を考えること以外に)物事が変化したときに何が起こるかを考えることです。たとえば、人がさまざまな製品に費やした金額を計算するとします。価格が変更される可能性がある場合、提案された表現(および追加情報なしの私の提案)ではこれを行うことはできません。その人は価格が異なるときに製品を購入した可能性があるためです。テンポラルデータベーステクニックはこれらの状況を一般的に処理できますが、これを完全に行うことはしばしばやり過ぎです。それでも、状況をより正確にモデル化する傾向があるため、一時データベースを確認することをお勧めします。そこから、どのデータ(もしあれば)が重要ではないかを検討することができるため、失う可能性があります。 (時間データベースは通常「挿入専用」です。)データが時間の経過とともにどのように変化するかを考慮に入れると、元の提案や私の提案とは異なるスキーマが必要になると思います。

2つのテーブル(AB)の間にジャンクションがあり、さらにそれらと別のテーブル(ABC)の間にジャンクションがあるとします。

ABは非常に小さいリレーションである可能性があるため、RAMに保持できますが、ABCは巨大で適合しません。したがって、ABに触れるクエリは、ABCへのクエリよりも高速になります。 ABおよびABCフィールドのみの個別のインデックスは、一意のABペアのインデックスよりもはるかに大きくなります。

OTOH FKでABCABに依存させません。代わりに、AB、およびCに直接依存するようにします。

1
9000

OPはこちら。

提供された回答を読み、主題についてさらに調査して考えた後、私のセットアップ自体には技術的に問題はないと結論付けました。しかし、実際的な問題に直面していたため、スキームの関係を少し修正することで解決策を見つけました。私の問題が私が使用していたJavaScript ORMに固有であるかどうか(つまり sequelize )か、他のORMにも存在するかどうかはわかりませんが、それにも関わらず、意味のある別の設定が見つかり、この問題を回避します。

したがって、私もここに自分の答えを投稿することにしました。同じような状況で誰かがつまずいた場合、おそらく私が行ったようにスキームを変更すると役立つでしょう(ただし、元のスキームは技術的に間違っていません)。

引用 SOでのリンクされた質問に対する自分の回答

古いアプローチの分析

まず第一に、私の質問で与えられた両方のジャンクションテーブルは、「単なる」ジャンクションテーブル以上のものでした。それらは単に、どの要素がどの要素に関連付けられているかを定義するためのツールではなく、より多くのものでした。

  • 彼らはまた、追加情報も持っていました(それぞれ「価格」と「思考」のフィールド)。

  • 最初の1つ、 Company_Product、他のテーブル自体との関係もありました。

厳密に言えば、これは技術的に間違っているわけではありませんが、同じものを表すようにデータベースを構造化するより自然な方法があります。さらに、この新しいアプローチにより、必要なクエリを作成することが非常に簡単になります。

解決策:新しいアプローチ

購入可能なアイテムと購入自体をモデル化していることがわかると、解決策は高くなります。この情報を多対多の関係のジャンクションテーブル内に "偽装"するのではなく、独自のテーブルを使用して、これらの情報をスキーマ内の明示的なエンティティとして保持します。

だから、最初に、明確にするために、モデルの名前を変更しましょう:

  • Companyは残りますCompany
  • ProductProductTypeになります
  • Company_ProductProductになります
  • Personは残りますPerson
  • Company_Product_PersonPurchaseになります

そして、それがわかります:

  • Productには、1つのCompanyと1つのProductTypeがあります。逆に、同じCompanyは複数のProductに関連付けることができ、同じProductTypeは複数のProductに関連付けることができます。
  • Purchaseには、1つのProductと1つのPersonがあります。逆に、同じProductは複数のPurchaseに関連付けることができ、同じProductは複数のPersonに関連付けることができます。

多対多の関係はもう存在しないことに注意してください。

[...]

単純なジャンクションテーブル以上のジャンクションテーブルを使用する代わりに、多対多の関係を取り除き、ジャンクションテーブルをスキームの本格的なエンティティに「昇格」させました。実際、テーブルは同じです。変化は関係とそれらを見る方法だけにありました。

0
Pedro A