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_productin questo caso. -
Ho aggiunto
serialcolonne come chiavi primarie sostitutive . In Postgres 10 o successivo considera unIDENTITYcolonna 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
integera 4 byte (o anche unbiginta 8 byte ) che con una stringa memorizzata cometextovarchar. -
Non utilizzare nomi di tipi di dati di base come
datecome 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
productessereproduct(oproduct_nameo 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 unabillsarebbe.bill_idprobabilmente sarà sufficiente in questo caso. -
priceè di tipo di datinumericper 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_producted è di tiponumericanche. Di nuovo,integerse 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_idobill_iddovrebbe cambiare, la modifica viene applicata a cascata a tutte le voci dipendenti inbill_producte niente si rompe. Questi sono solo riferimenti privi di significato.
Ho usato ancheON DELETE CASCADEperbill_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 aproductper contrassegnare invece le righe obsolete ("cancellazione graduale"). -
Tutte le colonne in questo esempio di base finiscono per essere
NOT NULL, quindiNULLi valori non sono ammessi. (Sì, tutti colonne - le colonne della chiave primaria sono definiteUNIQUE NOT NULLautomaticamente.) Questo perchéNULLi valori non avrebbero senso in nessuna delle colonne. Semplifica la vita di un principiante. Ma non te la caverai così facilmente, devi capireNULLcomunque maneggiare. Colonne aggiuntive potrebbero consentireNULLvalori, funzioni e join possono introdurreNULLvalori nelle query ecc. -
Leggi il capitolo su
CREATE TABLEnel 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_ido(product_id, bill_id)se hai domande che cercano un determinatoproduct_ide 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.