PostgreSQLに多対多の関係を実装する方法は?


95

タイトルは一目瞭然だと思います。多対多の関係を作成するには、PostgreSQLでテーブル構造をどのように作成しますか。

私の例:

Product(name, price);
Bill(name, date, Products);

2
billテーブルから製品を削除し、2つのフィールドを持つ「bill_products」という新しいテーブルを作成します。1つは製品を指し、もう1つは請求を指します。これら2つのフィールドをこの新しいテーブルの主キーにします。
マルクB

つまり、bill_products(bill、products); ?そして、両方ともPKですか?
Radu Gheorghiu

1
ええ。それらは個別にそれぞれのテーブルを指すFKであり、一緒に新しいテーブルのPKになります。
マルクB

したがって、bill_product(productはproduct.nameを参照し、billはbill.nameを参照し、(product、bill)の主キー)ですか?
Radu Gheorghiu

彼らは、ProductテーブルとBillテーブルのPKフィールドがどうなるかを指摘しました。
マルクB

回答:


298

SQL DDL(データ定義言語)ステートメントは次のようになります。

CREATE TABLE product (
  product_id serial PRIMARY KEY  -- implicit primary key constraint
, product    text NOT NULL
, price      numeric NOT NULL DEFAULT 0
);

CREATE TABLE bill (
  bill_id  serial PRIMARY KEY
, bill     text NOT NULL
, billdate date NOT NULL DEFAULT CURRENT_DATE
);

CREATE TABLE bill_product (
  bill_id    int REFERENCES bill (bill_id) ON UPDATE CASCADE ON DELETE CASCADE
, product_id int REFERENCES product (product_id) ON UPDATE CASCADE
, amount     numeric NOT NULL DEFAULT 1
, CONSTRAINT bill_product_pkey PRIMARY KEY (bill_id, product_id)  -- explicit pk
);

いくつかの調整を行いました:

  • N:Mの関係は、通常、別々のテーブルによって実現される- bill_productこの場合には。

  • 代理主キーserialとして列を追加しました。Postgres 10以降では、代わりにカラムを検討してください。見る:IDENTITY

    製品の名前はほとんど一意ではないため、これを強くお勧めします(適切な「自然キー」ではありません)。また、一意性の強制と外部キーでの列の参照は、通常、or として格納された文字列よりintegerも4バイト(または8バイトbigint)の方が安価です。textvarchar

  • などの基本データ型の名前を使用しないでくださいdateとしての識別子。これは可能ですが、スタイルが悪いため、エラーとエラーメッセージがわかりにくくなります。正しい、小文字の引用符で囲まれていない識別子を使用してください。予約語は使用せず、可能であれば、二重引用符で囲まれた大文字と小文字の識別子を避けてください。

  • 「名前」は良い名前ではありません。私は、テーブルの列を名前を変更productするproduct(またはproduct_nameまたは類似)。それはより良い命名規則です。それ以外の場合、クエリでいくつかのテーブルを結合すると(リレーショナルデータベースでは多くのことを行います) "name"という名前の複数の列ができ、列のエイリアスを使用して混乱を取り除く必要があります。それは役に立ちません。別の広く普及しているアンチパターンは、列名としての「id」だけです。
    の名前が​​どうなるかわかりませんbillbill_idこの場合はおそらく十分でしょう。

  • priceであるデータ・タイプ numeric分数を格納するために入力した正確(任意精度のタイプの代わりに、浮動小数点型)。整数のみを扱う場合は、そのようにしintegerます。たとえば、価格をセントとして保存できます。

  • amount"Products"あなたの質問で)リンクテーブルに入るbill_productと型であるnumericとしても。繰り返しintegerますが、整数のみを扱う場合。

  • 外部キーが表示さbill_productますか?変更をカスケードするために両方を作成しましたON UPDATE CASCADEproduct_idまたはbill_idを変更する必要がある場合、変更はすべての依存するエントリにカスケードされbill_product、何も壊れません。それらは、それら自体の重要性のない単なる参照です。
    私はまた使用しON DELETE CASCADEましたbill_id:請求書が削除された場合、その詳細はそれに伴って死にます。
    製品についてはそうではありません。請求書で使用されている製品を削除したくない場合。これを試みるとPostgresはエラーをスローします。product代わりに、もう1つの列を追加して、古い行をマーク(「ソフト削除」)します。

  • この基本的な例のすべての列はになるNOT NULLため、NULL値は許可されません。(はい、すべての列-主キー列はUNIQUE NOT NULL自動的に定義されます。)これNULLは、どの列でも値が意味をなさないためです。初心者の方の生活を楽にします。しかし、それほど簡単に逃げることはできないので、とにかくNULL取り扱いを理解する必要があります。列を追加すると、NULL値、関数、結合が許可されNULL、クエリなどに値が導入される可能性があります。

  • CREATE TABLEマニュアルの章をお読みください。

  • 主キーは、キー列に一意のインデックスを使用して実装されるため、PK列の条件を持つクエリが高速になります。ただし、キー列のシーケンスは複数列キーに関連しています。私の例でbill_productはPK on がオン(bill_id, product_id)になっているので、単に、product_idまたは(product_id, bill_id)指定されたproduct_idとnoを検索するクエリがある場合に、別のインデックスを追加することができますbill_id。見る:

  • マニュアルの索引に関する章を読んでください。


マッピングテーブルのインデックスを作成するにはどうすればよいbill_productですか?通常は次のようになりますCREATE INDEX idx_bill_product_id ON booked_rates(bill_id, product_id)。これは正解?
codyLine、2015年

1
@codyLine:このインデックスは、PKによって自動的に作成されます。
Erwin Brandstetter 2015年

1
@ErwinBrandstetter:product_id列のbill_productにインデックスを作成しないでください?
クリスチャン

2
@ ChristianB.Almeida:それは多くの場合に役立ちます、はい。索引付けについて少し追加しました。
Erwin Brandstetter、2015年

1
@Jakov:表の各請求書には1行しかありませんbill。で追加したアイテムごとの金額が必要bill_productです。
Erwin Brandstetter 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.