Ich glaube, der Titel ist selbsterklärend. Wie erstellen Sie die Tabellenstruktur in PostgreSQL, um eine Viele-zu-Viele-Beziehung herzustellen?
Mein Beispiel:
Product(name, price);
Bill(name, date, Products);
Ich glaube, der Titel ist selbsterklärend. Wie erstellen Sie die Tabellenstruktur in PostgreSQL, um eine Viele-zu-Viele-Beziehung herzustellen?
Mein Beispiel:
Product(name, price);
Bill(name, date, Products);
Antworten:
Die SQL DDL-Anweisungen (Data Definition Language) könnten folgendermaßen 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 einige Anpassungen vorgenommen:
Die n: m-Beziehung wird normalerweise durch eine separate Tabelle implementiert - bill_product
in diesem Fall.
Ich habe serial
Spalten als Ersatzprimärschlüssel hinzugefügt . Betrachten Sie in Postgres 10 oder höher stattdessen eine IDENTITY
Spalte . Sehen:
Ich kann das nur empfehlen, da der Name eines Produkts kaum eindeutig ist (kein guter "natürlicher Schlüssel"). Das Erzwingen der Eindeutigkeit und das Verweisen auf die Spalte in Fremdschlüsseln ist in der Regel mit einem 4-Byte integer
(oder sogar einem 8-Byte bigint
) billiger als mit einer als text
oder gespeicherten Zeichenfolge varchar
.
Sie keine Namen von Basisdatentypen wie verwenden date
als Bezeichner . Dies ist zwar möglich, hat aber einen schlechten Stil und führt zu verwirrenden Fehlern und Fehlermeldungen. Verwenden Sie legale, nicht zitierte Bezeichner in Kleinbuchstaben . Verwenden Sie niemals reservierte Wörter und vermeiden Sie doppelte Anführungszeichen für gemischte Groß- und Kleinschreibung, wenn Sie können.
"Name" ist kein guter Name. Ich habe die Spalte der Tabelle product
in product
( product_name
oder ähnlich) umbenannt. Das ist eine bessere Namenskonvention . Andernfalls , wenn Sie ein paar Tabellen in einer Join - Abfrage - etwas Sie tun eine Menge in einer relationalen Datenbank - Sie mehrere Spalten mit dem Namen „name“ am Ende mit und müssen Spaltenaliasnamen verwenden , um das Chaos in Ordnung bringen. Das ist nicht hilfreich. Ein weiteres weit verbreitetes Anti-Pattern wäre nur "id" als Spaltenname.
Ich bin mir nicht sicher, wie der Name eines bill
lauten würde. bill_id
wird in diesem Fall wahrscheinlich ausreichen.
price
ist vom Datentyp numeric
, um Bruchzahlen genau wie eingegeben zu speichern (beliebiger Genauigkeitstyp anstelle des Gleitkommatyps). Wenn Sie sich ausschließlich mit ganzen Zahlen beschäftigen, machen Sie das integer
. Sie können beispielsweise Preise als Cent speichern .
Das amount
( "Products"
in Ihrer Frage) geht in die Verknüpfungstabelle bill_product
und ist ebenfalls vom Typ numeric
. Auch hier gilt, integer
wenn Sie sich ausschließlich mit ganzen Zahlen befassen.
Sie sehen die Fremdschlüssel in bill_product
? Ich habe beide erstellt, um Änderungen zu kaskadieren : ON UPDATE CASCADE
. Wenn sich a product_id
oder bill_id
ändern sollte, wird die Änderung auf alle abhängigen Einträge in kaskadiert bill_product
und nichts wird unterbrochen. Dies sind nur Referenzen ohne eigene Bedeutung.
Ich habe auch verwendet ON DELETE CASCADE
für bill_id
: Wenn eine Rechnung gelöscht wird, sterben ihre Details damit.
Nicht so bei Produkten: 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
stattdessen eine weitere Spalte hinzufügen , um veraltete Zeilen zu markieren ("soft-delete").
Alle Spalten in diesem Basisbeispiel sind am Ende so NOT NULL
, dass NULL
Werte nicht zulässig sind. (Ja, alle Spalten - Primärschlüsselspalten werden UNIQUE NOT NULL
automatisch definiert .) Dies liegt daran, dass NULL
Werte in keiner der Spalten sinnvoll sind. Es erleichtert das Leben eines Anfängers. Aber Sie werden nicht so leicht davonkommen, Sie müssen sowieso die NULL
Handhabung verstehen . Zusätzliche Spalten können NULL
Werte zulassen , Funktionen und Verknüpfungen können NULL
Werte in Abfragen usw. einführen .
Lesen Sie das Kapitel CREATE TABLE
im Handbuch .
Primärschlüssel werden mit einem eindeutigen Index für die Schlüsselspalten implementiert , wodurch Abfragen mit Bedingungen für die PK-Spalte (n) schnell durchgeführt werden. Die Reihenfolge der Schlüsselspalten ist jedoch bei mehrspaltigen Schlüsseln relevant. Da in meinem Beispiel die PK aktiviert bill_product
ist (bill_id, product_id)
, möchten Sie möglicherweise einen weiteren Index für just hinzufügen, product_id
oder (product_id, bill_id)
wenn Sie Abfragen haben, die nach einem bestimmten product_id
und einem bestimmten suchen bill_id
. Sehen:
Lesen Sie das Kapitel über Indizes im Handbuch .
bill_product
? Normalerweise sollte es so aussehen : CREATE INDEX idx_bill_product_id ON booked_rates(bill_id, product_id)
. Ist das richtig?
bill
. Wir benötigen den Betrag pro hinzugefügtem Artikel in bill_product
.