Le istruzioni SQL DDL (data definition language) potrebbero avere il seguente aspetto:
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
);
Ho apportato alcune modifiche:
-
La relazione n:m è normalmente implementato da una tabella separata -
bill_product
in questo caso. -
Ho aggiunto
serial
colonne come chiavi primarie sostitutive . In Postgres 10 o successivo considera unIDENTITY
colonna invece. Vedi:- Rinomina in modo sicuro le tabelle utilizzando colonne di chiave primaria seriale
- Colonna tabella con incremento automatico
- https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/
Lo consiglio vivamente, perché il nome di un prodotto difficilmente è univoco (non è una buona "chiave naturale"). Inoltre, applicare l'univocità e fare riferimento alla colonna nelle chiavi esterne è in genere più economico con un
integer
a 4 byte (o anche unbigint
a 8 byte ) che con una stringa memorizzata cometext
ovarchar
. -
Non utilizzare nomi di tipi di dati di base come
date
come identificatori . Sebbene ciò sia possibile, è di cattivo stile e porta a errori e messaggi di errore confusi. Utilizzare identificatori legali, minuscoli e senza virgolette. Non usare mai parole riservate ed evita gli identificatori di maiuscole e minuscole tra virgolette se puoi. -
"nome" non è un buon nome. Ho rinominato la colonna della tabella
product
essereproduct
(oproduct_name
o simili). Questa è una migliore convenzione di denominazione . Altrimenti, quando unisci un paio di tabelle in una query, cosa che fai molto in un database relazionale - ti ritrovi con più colonne denominate "nome" e devi usare alias di colonna per risolvere il problema. Non è utile. Un altro anti-pattern diffuso sarebbe semplicemente "id" come nome di colonna.
Non sono sicuro di quale sia il nome di unabill
sarebbe.bill_id
probabilmente sarà sufficiente in questo caso. -
price
è di tipo di datinumeric
per memorizzare i numeri frazionari esattamente come immessi (tipo di precisione arbitraria invece di tipo a virgola mobile). Se ti occupi esclusivamente di numeri interi, crea quelinteger
. Ad esempio, potresti risparmiare prezzi come centesimi . -
L'
amount
("Products"
nella tua domanda) va nella tabella di collegamentobill_product
ed è di tiponumeric
anche. Di nuovo,integer
se tratti esclusivamente numeri interi. -
Vengono visualizzate le chiavi straniere in
bill_product
? Ho creato entrambi per le modifiche a cascata:ON UPDATE CASCADE
. Se unproduct_id
obill_id
dovrebbe cambiare, la modifica viene applicata a cascata a tutte le voci dipendenti inbill_product
e niente si rompe. Questi sono solo riferimenti privi di significato.
Ho usato ancheON DELETE CASCADE
perbill_id
:Se una fattura viene eliminata, i suoi dettagli muoiono con essa.
Non così per i prodotti:non si desidera eliminare un prodotto utilizzato in una fattura. Postgres genererà un errore se ci provi. Dovresti aggiungere un'altra colonna aproduct
per contrassegnare invece le righe obsolete ("cancellazione graduale"). -
Tutte le colonne in questo esempio di base finiscono per essere
NOT NULL
, quindiNULL
i valori non sono ammessi. (Sì, tutti colonne - le colonne della chiave primaria sono definiteUNIQUE NOT NULL
automaticamente.) Questo perchéNULL
i valori non avrebbero senso in nessuna delle colonne. Semplifica la vita di un principiante. Ma non te la caverai così facilmente, devi capireNULL
comunque maneggiare. Colonne aggiuntive potrebbero consentireNULL
valori, funzioni e join possono introdurreNULL
valori nelle query ecc. -
Leggi il capitolo su
CREATE TABLE
nel manuale. -
Le chiavi primarie sono implementate con un indice univoco sulle colonne chiave, che rende veloci le query con condizioni sulle colonne PK. Tuttavia, la sequenza delle colonne chiave è rilevante nelle chiavi multicolonna. Dal momento che il PK su
bill_product
è su(bill_id, product_id)
nel mio esempio, potresti voler aggiungere un altro indice solo suproduct_id
o(product_id, bill_id)
se hai domande che cercano un determinatoproduct_id
e nessunbill_id
. Vedi:- Chiave primaria composita PostgreSQL
- Un indice composito va bene anche per le query sul primo campo?
- Lavorare gli indici in PostgreSQL
-
Leggi il capitolo sugli indici nel manuale.