Les instructions SQL DDL (langage de définition de données) pourraient ressembler à ceci :
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
);
J'ai fait quelques ajustements :
-
La relation n:m est normalement implémenté par une table séparée -
bill_product
dans ce cas. -
J'ai ajouté
serial
colonnes comme clés primaires de substitution . Dans Postgres 10 ou version ultérieure, considérez uneIDENTITY
colonne à la place. Voir :- Renommer les tables en toute sécurité à l'aide de colonnes de clé primaire en série
- Colonne de tableau d'incrémentation automatique
- https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/
Je le recommande vivement, car le nom d'un produit n'est guère unique (ce n'est pas une bonne "clé naturelle"). De plus, appliquer l'unicité et référencer la colonne dans des clés étrangères est généralement moins cher avec un
integer
de 4 octets (ou même unbigint
de 8 octets ) qu'avec une chaîne stockée sous la formetext
ouvarchar
. -
N'utilisez pas de noms de types de données de base comme
date
comme identifiants . Bien que cela soit possible, c'est un mauvais style et conduit à des erreurs et des messages d'erreur confus. Utilisez des identifiants légaux, en minuscules et sans guillemets. N'utilisez jamais de mots réservés et évitez si possible les identificateurs à casse mixte entre guillemets. -
"nom" n'est pas un bon nom. J'ai renommé la colonne de la table
product
êtreproduct
(ouproduct_name
ou similaire). C'est une meilleure convention de nommage . Sinon, lorsque vous joignez quelques tables dans une requête - ce que vous faites beaucoup dans une base de données relationnelle - vous vous retrouvez avec plusieurs colonnes nommées "nom" et devez utiliser des alias de colonne pour régler le désordre. Ce n'est pas utile. Un autre anti-modèle répandu serait simplement "id" comme nom de colonne.
Je ne suis pas sûr du nom d'unebill
serait.bill_id
suffira probablement dans ce cas. -
price
est de type de donnéesnumeric
pour stocker les nombres fractionnaires précisément tels qu'ils ont été saisis (type de précision arbitraire au lieu de type à virgule flottante). Si vous traitez exclusivement des nombres entiers, faites de cetinteger
. Par exemple, vous pouvez enregistrer les prix en centimes . -
Le
amount
("Products"
dans votre question) va dans la table de liaisonbill_product
et est de typenumeric
aussi bien. Encore une fois,integer
si vous traitez exclusivement des nombres entiers. -
Vous voyez les clés étrangères dans
bill_product
? J'ai créé les deux pour cascader les changements :ON UPDATE CASCADE
. Si unproduct_id
oubill_id
doit changer, la modification est répercutée sur toutes les entrées dépendantes dansbill_product
et rien ne casse. Ce ne sont que des références sans signification propre.
J'ai également utiliséON DELETE CASCADE
pourbill_id
:Si une facture est supprimée, ses détails disparaissent avec elle.
Ce n'est pas le cas pour les produits :vous ne voulez pas supprimer un produit qui est utilisé dans une facture. Postgres lancera une erreur si vous essayez cela. Vous ajouteriez une autre colonne àproduct
pour marquer les lignes obsolètes ("soft-delete") à la place. -
Toutes les colonnes de cet exemple de base finissent par être
NOT NULL
, doncNULL
les valeurs ne sont pas autorisées. (Oui, tous colonnes - les colonnes de clé primaire sont définiesUNIQUE NOT NULL
automatiquement.) C'est parce queNULL
les valeurs n'auraient de sens dans aucune des colonnes. Cela facilite la vie d'un débutant. Mais vous ne vous en sortirez pas si facilement, vous devez comprendreNULL
manipulation de toute façon. Des colonnes supplémentaires peuvent autoriserNULL
les valeurs, les fonctions et les jointures peuvent introduireNULL
valeurs dans les requêtes, etc. -
Lire le chapitre sur
CREATE TABLE
dans le manuel. -
Les clés primaires sont implémentées avec un index unique sur les colonnes clés, cela accélère les requêtes avec des conditions sur la ou les colonnes PK. Cependant, la séquence des colonnes de clé est pertinente dans les clés multicolonnes. Depuis le PK sur
bill_product
est sur(bill_id, product_id)
dans mon exemple, vous pouvez ajouter un autre index uniquement surproduct_id
ou(product_id, bill_id)
si vous avez des requêtes à la recherche d'unproduct_id
donné et pas debill_id
. Voir :- Clé primaire composite PostgreSQL
- Un index composite est-il également adapté aux requêtes sur le premier champ ?
- Travail des index dans PostgreSQL
-
Lisez le chapitre sur les index dans le manuel.