web-dev-qa-db-ja.com

オプションの関係を使用した3者間関連のモデリング

ビジネスルール

次の関係を表す3つのテーブル(パーティーカテゴリーおよび製品)があります。

  • 製品は、1対多のカテゴリで分類されます
  • カテゴリは、1つまたは複数のゼロを分類します製品

次に、パーティー関係があります:

  • 製品は1対1で分類されますパーティー
  • パーティーは1対多の製品を分類します

つまり、製品にカテゴリを割り当てる必要はありませんが、製品にはそれぞれパーティを割り当てる必要があります。

カテゴリparty_idは、関連するために製品party_idと一致する必要があります。

編集

以下は、@ damir-sudarevicのソリューション提案に基づいた、上記のビジネスルールの修正です。

  • Categorypartyによって定義されます。

    • categoryは、1つのpartyによって定義されます。
    • partyは複数のcategoryを定義する場合があります。
  • Productpartyによって分類されます。

    • productは、1つのpartyによって正確に分類されます。
    • partyは複数のproductを分類する場合があります。
  • Productは、categoryによってpartyに分類されます。

    • productは、複数のcategoryに分類される場合があります。
    • 複数のproductが同じcategoryに分類される場合があります。
    • productは、party内のcategoryによって分類され、そのcategoryは、そのpartyによって定義されます。

設計案

私が見つけた提案に基づいて最初のデザインを作成しました here ですが、製品カテゴリーの両方にparty_idを強制したいので、それは完全には適用できませんand関係で。

Three-way association design proposal

設計を多少簡略化する2番目の提案をしましたが、party_idProduct-Category関係に適用する方法がわかりません。

enter image description here


最新の設計に基づくSQL

コメントとソリューションの提案に基づいて、テーブルとそれらの関係を作成するための簡略化されたSQLを追加しました。

CREATE TABLE IF NOT EXISTS parties (
  id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (id));

CREATE TABLE IF NOT EXISTS categories (
  id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  name_key VARCHAR(255) NOT NULL,
  party_id INT(10) UNSIGNED NOT NULL,
  parent_id INT(10) UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (id, party_id),
  INDEX fk_categories_parent_category_idx (parent_id ASC),
  UNIQUE INDEX name_key_UNIQUE (name_key ASC, party_id ASC),
  INDEX fk_categories_party_idx (party_id ASC),
  CONSTRAINT fk_categories_parent_category
    FOREIGN KEY (parent_id)
    REFERENCES categories (id)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT fk_categories_party
    FOREIGN KEY (party_id)
    REFERENCES parties (id)
    ON DELETE CASCADE
    ON UPDATE CASCADE);

CREATE TABLE IF NOT EXISTS products (
  id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  party_id INT(10) UNSIGNED NOT NULL,
  product_code VARCHAR(50) NOT NULL,
  PRIMARY KEY (id, party_id),
  UNIQUE INDEX product_code_UNIQUE (product_code ASC, party_id ASC),
  INDEX fk_products_party_idx (party_id ASC),
  CONSTRAINT fk_products_party
    FOREIGN KEY (party_id)
    REFERENCES parties (id)
    ON DELETE CASCADE
    ON UPDATE CASCADE);

CREATE TABLE IF NOT EXISTS product_category (
  product_id INT(10) UNSIGNED NOT NULL,
  category_id INT(10) UNSIGNED NOT NULL,
  party_id INT(10) UNSIGNED NOT NULL,
  PRIMARY KEY (product_id, category_id),
  INDEX fk_product_category_product_idx (product_id ASC, party_id ASC),
  INDEX fk_product_category_category_idx (category_id ASC, party_id ASC),
  CONSTRAINT fk_product_category_product
    FOREIGN KEY (product_id , party_id)
    REFERENCES products (id , party_id)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  CONSTRAINT fk_product_category_category
    FOREIGN KEY (category_id , party_id)
    REFERENCES categories (id , party_id)
    ON DELETE CASCADE
    ON UPDATE CASCADE);

質問

party_idを適用せずに、アプリケーションレイヤーがproductcategoryに割り当てるリスクを回避するために、3方向の関連付けテーブルを正しく設定するにはどうすればよいですか?

1
Jonah

Categoryparty_idは、関連付けるためにProductparty_idと一致する必要があります。

まあ、あなたの問題の表現によれば、Categoryparty_idは必要ありません。
混乱はおそらく不正確な表現に起因し、述語と制約を1つの文にブレンドしています。
例えば:

  • 製品は1対1のパーティーによって分類されます。
  • パーティーは1対多の製品を分類します。

より明確に言い表すことができます:

  • Productpartyによって分類されます。
  • productは、1つのpartyによって分類されます。
  • partyは複数のproductを分類する場合があります。

この表現は、使用可能なモデルに直接つながります(述語、constraints)。


オプション1

-- Party PTY exists.
--
party {PTY}
   PK {PTY}

各製品は正確に1つのパーティによって分類されます。各パーティについて、そのパーティは複数の製品を分類する場合があります。

-- Product PRO, classified by party PTY exists.
--
product {PRO, PTY}
     PK {PRO}

FK {PTY} REFERENCES party {PTY}
-- Category CAT exists.
--
category {CAT}
      PK {CAT}

各製品について、その製品は複数のカテゴリーに分類される場合があります。各カテゴリーについて、複数の製品がそのカテゴリーに属するものとして分類される場合があります。

-- Product PRO is classified in category CAT.
--
product_category {PRO, CAT}
              PK {PRO, CAT}

FK1 {PRO} REFERENCES product  {PRO}
FK2 {CAT} REFERENCES category {CAT}

オプション2

マッチングパーティーが知られる前に製品が知られている可能性があります。次にバリエーション:

-- Party PTY exists.
--
party {PTY}
   PK {PTY}
-- Product PRO exists.
--
product {PRO}
     PK {PRO}
-- Product PRO is classified by party PTY.
--
product_party {PRO, PTY}
           PK {PRO}

FK1 {PRO} REFERENCES product {PRO}
FK2 {PTY} REFERENCES party   {PTY}
-- Category CAT exists.
--
category {CAT}
      PK {CAT}
-- Product PRO is classified in category CAT.
--
product_category {PRO, CAT}
              PK {PRO, CAT}

FK1 {PRO} REFERENCES product_party {PRO}

FK2 {CAT} REFERENCES category {CAT}

編集

いくつかのコメントの後:

  • Categorypartyによって定義されます。

    • categoryは1つのpartyによって定義されます。
    • partyは複数のcategoryを定義する場合があります。
  • Productpartyによって分類されます。

    • productは、1つのpartyによって分類されます。
    • partyは複数のproductを分類する場合があります。
  • Productcategoryによってpartyに分類されます。

    • productは複数のcategoryに分類される場合があります。
    • 複数のproductが同じcategoryに分類される場合があります。
    • productpartycategoryによって分類される場合、そのcategoryはそのpartyによって定義されます

オプション3

Partycategoryおよびproductの前に存在する必要があります。

-- Party PTY exists.
--
party {PTY}
   PK {PTY}


-- Product PRO, classified by party PTY exists.
--
product {PRO, PTY}
     PK {PRO}
     SK {PRO, PTY}

     FK {PTY} REFERENCES party {PTY}


-- Category CAT, defined by party PTY exists.
--
category {CAT, PTY}
      PK {CAT}
      SK {CAT, PTY}

      FK {PTY} REFERENCES party {PTY}


-- Product PRO is classified in category CAT
-- by party PTY.
--
product_category {PRO, CAT, PTY}
              PK {PRO, CAT}

      FK1 {PRO, PTY} REFERENCES product  {PRO, PTY}
      FK2 {CAT, PTY} REFERENCES category {CAT, PTY}

オプション4

productcategorypartyとは独立して存在できる場合。

-- Party PTY exists.
--
party {PTY}
   PK {PTY}


-- Product PRO exists.
--
product {PRO}
     PK {PRO}


-- Category CAT exists.
--
category {CAT}
      PK {CAT}


-- Product PRO is classified by party PTY.
--
product_party {PRO, PTY}
           PK {PRO}
           SK {PRO, PTY}

      FK1 {PRO} REFERENCES product {PRO}
      FK2 {PTY} REFERENCES party   {PTY}


-- Category CAT is defined by party PTY.
--
category_party {CAT, PTY}
            PK {CAT}
            SK {CAT, PTY}

      FK1 {CAT} REFERENCES category {CAT}
      FK2 {PTY} REFERENCES party    {PTY}


-- Product PRO is classified in category CAT
-- by party PTY.
--
product_category {PRO, CAT, PTY}
              PK {PRO, CAT}

                 FK1 {PRO, PTY} REFERENCES
      product_party  {PRO, PTY}

                 FK2 {CAT, PTY} REFERENCES
      category_party {CAT, PTY}

注意:

All attributes (columns) NOT NULL

PK = Primary Key
AK = Alternate Key   (Unique)
SK = Proper Superkey (Unique)
FK = Foreign Key
1
Damir Sudarevic

モデル#1を使用すると、データベース自体が指定された制約を強制するように作成できると信じています(つまり、categoryproductは、両方が関連している場合にのみ関連している可能性があります同じparty)に次の制約を使用します。

  • category_partyの主キーはcategory_idparty_idで構成されます。これにより、categoriespartiesの間にオプションの多対多の関係が作成されます。これは次のことを意味します。
    • A categoryは、1対多partiesで分類されます。
    • パーティーは、1対多のカテゴリを分類します。
  • product_partyの主キーはproduct_idparty_idで構成されます。これにより、categoriespartiesの間にオプションの多対多の関係が作成されます。これは次のことを意味します。
    • 製品は、1対多パーティーで分類されます。これは、指定された要件を満たすために以下でオーバーライドされます。
    • パーティーは、1対多製品で分類されます。
  • product_partyproduct_idには一意の制約があります。これにより、パーティー製品の関係は、多対多ではなく1対多になります。これは次のことを意味します。

    • productは1対1のpartyで分類され、この関係の以前に述べられたカーディナリティを上書きします。
  • product_category_assignmentの主キーは、category_idproduct_idおよびparty_idで構成されます。

  • product_category_assignmentからcategory_partyへの外部キーはcategory_idparty_idで構成されます
  • product_category_assignmentからproduct_partyへの外部キーはproduct_idparty_idで構成されます
  • categoryおよびproductは、共通のpartyに関連付けられている場合にのみ関連付けることができるという制約が適用されます。これは、product_category_assignment 1つだけparty_idが含まれています。これは、両方の外部キー関係で使用されます。

ここでカバーされていない唯一の要件はproductrequiredであることです関連パーティー(つまり、パーティーはオプションではありません)。

データベースのstructureで以下が許可されるため、モデル#2を使用すると、制約を適用するためにはるかに多くのコード(つまり、醜いストアドプロシージャ?)が必要になります。

  • categoryには、1つのparty_idを指定できます(例:1)。
  • productには、異なるparty_idを含めることができます(例:2)。
  • product_categoryには、さらに3番目のparty_idを含めることができます(例:3)。

上記の私の回答に若干の影響を与える可能性のあるいくつかの明記されていない要件があるように見えることを指摘しておきます。例えば...

  • categorypartyの関係の正しいカーディナリティは何ですか?あなたが提示した2つのモデルは、この点で異なります。
    • モデル#1では、カテゴリをゼロ、1つまたは複数のパーティに関連付けることができます。
    • モデル#2は、各カテゴリに対して、ゼロまたは1つのパーティーのみを許可します。
  • categoryには(少なくとも)1つのpartyが必要ですか、それともその関係はオプションですか?これは、説明またはモデルのいずれからも明確ではありません。

私は常に、データモデルで重要な3つのことがあると述べてきました:詳細、DeTaiLsおよび[〜#〜]詳細[〜#〜]。場合によっては、複雑さに関するすべての関係の詳細を明らかにすることで、データモデルを設計する最良の方法を見つけることができます。このような複雑な関係は、欠落しているエンティティ(テーブル)を指す場合があります。複雑な要件は、データベースの構造的制約のみを使用して実施を無視し、ジョブを完了するために少しのコード(たとえば、ストアドプロシージャ?)を必要とする場合があります。

お役に立てば幸いです。

1
RickC