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 eineIDENTITY
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 alstext
gespeicherten String odervarchar
. -
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
umbenanntproduct
sein (oderproduct_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 einbill
heißt wäre.bill_id
wird in diesem Fall wahrscheinlich ausreichen. -
price
ist vom Datentypnumeric
um Bruchzahlen genau wie eingegeben zu speichern (beliebiger Genauigkeitstyp anstelle von Fließkommatyp). Wenn Sie ausschließlich mit ganzen Zahlen arbeiten, machen Sie dieseinteger
. Sie können beispielsweise Preise in Cent speichern . -
Der
amount
("Products"
in Ihrer Frage) geht in die Verknüpfungstabellebill_product
und ist vom Typnumeric
auch. Wiederinteger
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 eineproduct_id
oderbill_id
sollte sich ändern, wird die Änderung auf alle abhängigen Einträge inbill_product
kaskadiert und nichts geht kaputt. Das sind nur Referenzen ohne eigene Bedeutung.
Ich habe auchON DELETE CASCADE
verwendet fürbill_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ürdenproduct
eine weitere Spalte hinzufügen um stattdessen veraltete Zeilen zu markieren ("soft-delete"). -
Alle Spalten in diesem einfachen Beispiel sind am Ende
NOT NULL
, alsoNULL
Werte sind nicht erlaubt. (Ja, alle Spalten - Primärschlüsselspalten sind definiertUNIQUE NOT NULL
automatisch.) Das liegt daran, dassNULL
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 musstNULL
verstehen Umgang sowieso. Zusätzliche Spalten könntenNULL
zulassen Werte, Funktionen und Joins könnenNULL
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ürproduct_id
hinzufügen oder(product_id, bill_id)
wenn Sie Abfragen haben, die nach einer bestimmtenproduct_id
suchen und keinebill_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.