De SQL DDL-instructies (data definition language) kunnen er als volgt uitzien:
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
);
Ik heb een paar aanpassingen gedaan:
-
De n:m-relatie wordt normaal gesproken geïmplementeerd door een aparte tabel -
bill_product
in dit geval. -
Ik heb
serial
toegevoegd kolommen als primaire surrogaatsleutels . Overweeg in Postgres 10 of later eenIDENTITY
kolom in plaats daarvan. Zie:- Tabellen veilig hernoemen met seriële primaire-sleutelkolommen
- Tabelkolom automatisch verhogen
- https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/
Ik raad dat ten zeerste aan, omdat de naam van een product nauwelijks uniek is (geen goede "natuurlijke sleutel"). Ook is het afdwingen van uniciteit en het verwijzen naar de kolom in externe sleutels doorgaans goedkoper met een
integer
van 4 bytes (of zelfs een 8-bytebigint
) dan met een string die is opgeslagen alstext
ofvarchar
. -
Gebruik geen namen van basisgegevenstypen zoals
date
als ID's . Hoewel dit mogelijk is, is het een slechte stijl en leidt het tot verwarrende fouten en foutmeldingen. Gebruik legale, niet-geciteerde identificatiecodes in kleine letters. Gebruik nooit gereserveerde woorden en vermijd indien mogelijk dubbele aanhalingstekens tussen hoofdletters. -
"naam" is geen goede naam. Ik hernoemde de kolom van de tabel
product
omproduct
te zijn (ofproduct_name
of vergelijkbaar). Dat is een betere naamgevingsconventie . Anders, wanneer u een aantal tabellen samenvoegt in een query - wat u vaak doet in een relationele database - je krijgt uiteindelijk meerdere kolommen met de naam "naam" en moet kolomaliassen gebruiken om de rommel op te lossen. Dat is niet handig. Een ander wijdverbreid anti-patroon zou gewoon "id" zijn als kolomnaam.
Ik weet niet zeker wat de naam is van eenbill
zou zijn.bill_id
zal in dit geval waarschijnlijk volstaan. -
price
is van gegevenstypenumeric
om gebroken getallen precies op te slaan zoals ze zijn ingevoerd (willekeurig precisietype in plaats van floating point type). Als je uitsluitend met hele getallen werkt, maak dan datinteger
. U kunt bijvoorbeeld prijzen opslaan als cent . -
Het
amount
("Products"
in uw vraag) gaat naar de koppelingstabelbill_product
en is van het typenumeric
ook. Nogmaals,integer
als u uitsluitend met gehele getallen werkt. -
Je ziet de buitenlandse sleutels in
bill_product
? Ik heb beide gemaakt om wijzigingen trapsgewijs te wijzigen:ON UPDATE CASCADE
. Als eenproduct_id
ofbill_id
zou moeten veranderen, wordt de wijziging doorgevoerd in alle afhankelijke items inbill_product
en er gaat niets kapot. Dat zijn slechts verwijzingen zonder eigen betekenis.
Ik heb ookON DELETE CASCADE
gebruikt voorbill_id
:Als een rekening wordt verwijderd, verdwijnen de details ervan.
Niet zo voor producten:u wilt geen product verwijderen dat in een rekening wordt gebruikt. Postgres zal een foutmelding geven als u dit probeert. Je zou nog een kolom toevoegen aanproduct
om in plaats daarvan verouderde rijen te markeren ("soft-delete"). -
Alle kolommen in dit basisvoorbeeld worden uiteindelijk
NOT NULL
, dusNULL
waarden zijn niet toegestaan. (Ja, allemaal kolommen - primaire sleutelkolommen zijn gedefinieerdUNIQUE NOT NULL
automatisch.) Dat komt omdatNULL
waarden zouden in geen van de kolommen logisch zijn. Het maakt het leven van een beginner gemakkelijker. Maar je zult niet zo gemakkelijk wegkomen, je moetNULL
begrijpen toch behandelen. Extra kolommen kunnenNULL
allow toestaan waarden, functies en joins kunnenNULL
introduce introduceren waarden in zoekopdrachten enz. -
Lees het hoofdstuk over
CREATE TABLE
in de handleiding. -
Primaire sleutels zijn geïmplementeerd met een unieke index op de sleutelkolommen, die query's met voorwaarden op de PK-kolom(men) snel maakt. De volgorde van sleutelkolommen is echter relevant in sleutels met meerdere kolommen. Sinds de PK op
bill_product
is op(bill_id, product_id)
in mijn voorbeeld wil je misschien nog een index toevoegen op slechtsproduct_id
of(product_id, bill_id)
als je vragen hebt op zoek naar een gegevenproduct_id
en geenbill_id
. Zie:- PostgreSQL samengestelde primaire sleutel
- Is een samengestelde index ook goed voor zoekopdrachten op het eerste veld?
- Werking van indexen in PostgreSQL
-
Lees het hoofdstuk over indexen in de handleiding.