PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Come implementare una relazione molti-a-molti in PostgreSQL?

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 un IDENTITY 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 un bigint a 8 byte ) che con una stringa memorizzata come text o varchar .

  • 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 essere product (o product_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 una bill sarebbe. bill_id probabilmente sarà sufficiente in questo caso.

  • price è di tipo di dati numeric 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 quel integer . Ad esempio, potresti risparmiare prezzi come centesimi .

  • L'amount ("Products" nella tua domanda) va nella tabella di collegamento bill_product ed è di tipo numeric 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 un product_id o bill_id dovrebbe cambiare, la modifica viene applicata a cascata a tutte le voci dipendenti in bill_product e niente si rompe. Questi sono solo riferimenti privi di significato.
    Ho usato anche ON DELETE CASCADE per bill_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 a product per contrassegnare invece le righe obsolete ("cancellazione graduale").

  • Tutte le colonne in questo esempio di base finiscono per essere NOT NULL , quindi NULL i valori non sono ammessi. (Sì, tutti colonne - le colonne della chiave primaria sono definite UNIQUE 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 capire NULL comunque maneggiare. Colonne aggiuntive potrebbero consentire NULL valori, funzioni e join possono introdurre NULL 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 su product_id o (product_id, bill_id) se hai domande che cercano un determinato product_id e nessun bill_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.