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_productin diesem Fall. -
Ich habe
serialhinzugefügt Spalten als Ersatz-Primärschlüssel . Betrachten Sie in Postgres 10 oder höher eineIDENTITYSpalte 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-
integernormalerweise billiger (oder sogar ein 8-Byte-bigint) als mit einem alstextgespeicherten String odervarchar. -
Verwenden Sie keine Namen grundlegender Datentypen wie
dateals 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
productumbenanntproductsein (oderproduct_nameoder ä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 einbillheißt wäre.bill_idwird in diesem Fall wahrscheinlich ausreichen. -
priceist vom Datentypnumericum 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_productund ist vom Typnumericauch. Wiederintegerwenn 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_idoderbill_idsollte sich ändern, wird die Änderung auf alle abhängigen Einträge inbill_productkaskadiert und nichts geht kaputt. Das sind nur Referenzen ohne eigene Bedeutung.
Ich habe auchON DELETE CASCADEverwendet 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ürdenproducteine weitere Spalte hinzufügen um stattdessen veraltete Zeilen zu markieren ("soft-delete"). -
Alle Spalten in diesem einfachen Beispiel sind am Ende
NOT NULL, alsoNULLWerte sind nicht erlaubt. (Ja, alle Spalten - Primärschlüsselspalten sind definiertUNIQUE NOT NULLautomatisch.) Das liegt daran, dassNULLWerte würden in keiner der Spalten Sinn machen. Es macht das Leben eines Anfängers einfacher. Aber so einfach kommst du nicht davon, du musstNULLverstehen Umgang sowieso. Zusätzliche Spalten könntenNULLzulassen Werte, Funktionen und Joins könnenNULLeinführen Werte in Abfragen etc. -
Lesen Sie das Kapitel über
CREATE TABLEim 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_productist auf(bill_id, product_id)In meinem Beispiel möchten Sie vielleicht einen weiteren Index nur fürproduct_idhinzufügen oder(product_id, bill_id)wenn Sie Abfragen haben, die nach einer bestimmtenproduct_idsuchen 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.