PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Wie implementiert man eine Viele-zu-Viele-Beziehung in PostgreSQL?

Die SQL DDL (Data Definition Language)-Anweisungen könnten wie folgt aussehen:

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
);

Ich habe ein paar Anpassungen vorgenommen:

  • Die n:m-Beziehung wird normalerweise durch eine separate Tabelle implementiert - bill_product in diesem Fall.

  • Ich habe serial hinzugefügt Spalten als Ersatz-Primärschlüssel . Betrachten Sie in Postgres 10 oder höher eine IDENTITY Spalte stattdessen. Siehe:

    • Tabellen mit seriellen Primärschlüsselspalten sicher umbenennen
    • Tabellenspalte automatisch erhöhen
    • https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/

    Ich empfehle das sehr, da der Name eines Produkts kaum eindeutig ist (kein guter "natürlicher Schlüssel"). Außerdem ist das Erzwingen der Eindeutigkeit und das Referenzieren der Spalte in Fremdschlüsseln mit einer 4-Byte-integer normalerweise billiger (oder sogar ein 8-Byte-bigint ) als mit einem als text gespeicherten String oder varchar .

  • Verwenden Sie keine Namen grundlegender Datentypen wie date als Identifikatoren . Das ist zwar möglich, aber schlechter Stil und führt zu verwirrenden Fehlern und Fehlermeldungen. Verwenden Sie zulässige Bezeichner in Kleinbuchstaben und ohne Anführungszeichen. Verwenden Sie niemals reservierte Wörter und vermeiden Sie nach Möglichkeit gemischte Groß- und Kleinschreibung in doppelten Anführungszeichen.

  • "Name" ist kein guter Name. Ich habe die Spalte der Tabelle product umbenannt product sein (oder product_name oder ähnliches). Das ist eine bessere Namenskonvention . Andernfalls, wenn Sie ein paar Tabellen in einer Abfrage zusammenführen – was Sie häufig tun in einer relationalen Datenbank - Sie haben am Ende mehrere Spalten mit dem Namen "Name" und müssen Spaltenaliase verwenden, um das Durcheinander zu sortieren. Das ist nicht hilfreich. Ein weiteres weit verbreitetes Anti-Pattern wäre einfach "id" als Spaltenname.
    Ich bin mir nicht sicher, wie ein bill heißt wäre. bill_id wird in diesem Fall wahrscheinlich ausreichen.

  • price ist vom Datentyp numeric um Bruchzahlen genau wie eingegeben zu speichern (beliebiger Genauigkeitstyp anstelle von Fließkommatyp). Wenn Sie ausschließlich mit ganzen Zahlen arbeiten, machen Sie diese integer . Sie können beispielsweise Preise in Cent speichern .

  • Der amount ("Products" in Ihrer Frage) geht in die Verknüpfungstabelle bill_product und ist vom Typ numeric auch. Wieder integer wenn Sie ausschließlich mit ganzen Zahlen arbeiten.

  • Sie sehen die Fremdschlüssel in bill_product ? Ich habe beide erstellt, um Änderungen zu kaskadieren:ON UPDATE CASCADE . Wenn eine product_id oder bill_id sollte sich ändern, wird die Änderung auf alle abhängigen Einträge in bill_product kaskadiert und nichts geht kaputt. Das sind nur Referenzen ohne eigene Bedeutung.
    Ich habe auch ON DELETE CASCADE verwendet für bill_id :Wenn eine Rechnung gelöscht wird, sterben ihre Details mit.
    Nicht so für Produkte:Sie möchten kein Produkt löschen, das in einer Rechnung verwendet wird. Postgres gibt einen Fehler aus, wenn Sie dies versuchen. Sie würden product eine weitere Spalte hinzufügen um stattdessen veraltete Zeilen zu markieren ("soft-delete").

  • Alle Spalten in diesem einfachen Beispiel sind am Ende NOT NULL , also NULL Werte sind nicht erlaubt. (Ja, alle Spalten - Primärschlüsselspalten sind definiert UNIQUE NOT NULL automatisch.) Das liegt daran, dass NULL Werte würden in keiner der Spalten Sinn machen. Es macht das Leben eines Anfängers einfacher. Aber so einfach kommst du nicht davon, du musst NULL verstehen Umgang sowieso. Zusätzliche Spalten könnten NULL zulassen Werte, Funktionen und Joins können NULL einführen Werte in Abfragen etc.

  • Lesen Sie das Kapitel über CREATE TABLE im Handbuch.

  • Primärschlüssel werden mit einem eindeutigen Index implementiert auf den Schlüsselspalten, was Abfragen mit Bedingungen auf der/den PK-Spalte(n) schnell macht. Bei mehrspaltigen Schlüsseln ist jedoch die Reihenfolge der Schlüsselspalten relevant. Seit dem PK auf bill_product ist auf (bill_id, product_id) In meinem Beispiel möchten Sie vielleicht einen weiteren Index nur für product_id hinzufügen oder (product_id, bill_id) wenn Sie Abfragen haben, die nach einer bestimmten product_id suchen und keine bill_id . Siehe:

    • Zusammengesetzter PostgreSQL-Primärschlüssel
    • Ist ein zusammengesetzter Index auch gut für Abfragen im ersten Feld?
    • Arbeiten von Indizes in PostgreSQL
  • Lesen Sie das Kapitel über Indizes im Handbuch.