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_productin diesem Fall.
Ich habe serialSpalten als Ersatzprimärschlüssel hinzugefügt . Betrachten Sie in Postgres 10 oder höher stattdessen eine IDENTITYSpalte . 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 textoder gespeicherten Zeichenfolge varchar.
Sie keine Namen von Basisdatentypen wie verwenden dateals 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 productin product( product_nameoder ä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 billlauten würde. bill_idwird in diesem Fall wahrscheinlich ausreichen.
priceist 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_productund ist ebenfalls vom Typ numeric. Auch hier gilt, integerwenn 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_idoder bill_idändern sollte, wird die Änderung auf alle abhängigen Einträge in kaskadiert bill_productund nichts wird unterbrochen. Dies sind nur Referenzen ohne eigene Bedeutung.
Ich habe auch verwendet ON DELETE CASCADEfü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 productstattdessen eine weitere Spalte hinzufügen , um veraltete Zeilen zu markieren ("soft-delete").
Alle Spalten in diesem Basisbeispiel sind am Ende so NOT NULL, dass NULLWerte nicht zulässig sind. (Ja, alle Spalten - Primärschlüsselspalten werden UNIQUE NOT NULLautomatisch definiert .) Dies liegt daran, dass NULLWerte 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 NULLHandhabung verstehen . Zusätzliche Spalten können NULLWerte zulassen , Funktionen und Verknüpfungen können NULLWerte in Abfragen usw. einführen .
Lesen Sie das Kapitel CREATE TABLEim 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_productist (bill_id, product_id), möchten Sie möglicherweise einen weiteren Index für just hinzufügen, product_idoder (product_id, bill_id)wenn Sie Abfragen haben, die nach einem bestimmten product_idund 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.