web-dev-qa-db-ja.com

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

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

私の例:

Product(name, price);
Bill(name, date, Products);
64
Radu Gheorghiu

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 列を代理主キーとして追加しました。製品の名前はほとんど一意ではないため、これを強くお勧めします。また、4バイトのintegerを使用すると、textまたはvarcharとして保存された文字列を使用するよりも、一意性を強制して外部キーの列を参照する方がはるかに安価です。
    Postgres 10以降では、代わりに IDENTITY を検討してください。詳細:

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

  • nameは良い名前ではありません。テーブルnameproduct列の名前をproductに変更しました。それは、より良い命名規則です。それ以外の場合、クエリでいくつかのテーブルを結合する場合-リレーショナルデータベースでa lotを実行すると、nameという名前の複数の列になり、列エイリアスを使用して並べ替える必要があります混乱を。それは役に立たない。別の広範なアンチパターンは、列名としてidだけです。
    billの名前がわからない。この場合、bill_idnameになります。

  • pricedata typenumericで、小数を保存します入力されたとおりに正確(浮動小数点型ではなく任意の精度型)。整数のみを扱う場合は、integerにしてください。たとえば、価格はセントとしてと保存できます。

  • amount(質問では"Products")はリンクテーブルbill_productに入り、タイプもnumericになります。繰り返しますが、整数のみを扱う場合はintegerです。

  • bill_product?にforeign keysが表示されます変更をカスケードするために両方を作成しました(ON UPDATE CASCADE):product_idまたはbill_idを変更する必要がある場合、変更はbill_productの依存するすべてのエントリにカスケードされ、何も壊れません。
    ON DELETE CASCADEbill_idに使用しました。請求書を削除すると、詳細も一緒に削除されます。
    製品についてはそうではありません:請求書で使用されている製品を削除したくありません。これを試みると、Postgresはエラーをスローします。代わりにproductに別の列を追加して、古い行をマークします。

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

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

  • 主キーは、キー列に一意のindexを使用して実装されます。これにより、PK列の条件を持つクエリが高速になります。ただし、キー列の順序は複数列キーに関連しています。 bill_productのPKは私の例では(bill_id, product_id)にあるため、product_idを探して(product_id, bill_id)を探しているクエリがある場合は、product_idまたはbill_idだけに別のインデックスを追加できます。詳細:

  • マニュアルのインデックスに関する の章をお読みください

233