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_productin dit geval. -
Ik heb
serialtoegevoegd kolommen als primaire surrogaatsleutels . Overweeg in Postgres 10 of later eenIDENTITYkolom 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
integervan 4 bytes (of zelfs een 8-bytebigint) dan met een string die is opgeslagen alstextofvarchar. -
Gebruik geen namen van basisgegevenstypen zoals
dateals 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
productomproductte zijn (ofproduct_nameof 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 eenbillzou zijn.bill_idzal in dit geval waarschijnlijk volstaan. -
priceis van gegevenstypenumericom 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_producten is van het typenumericook. Nogmaals,integerals 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_idofbill_idzou moeten veranderen, wordt de wijziging doorgevoerd in alle afhankelijke items inbill_producten er gaat niets kapot. Dat zijn slechts verwijzingen zonder eigen betekenis.
Ik heb ookON DELETE CASCADEgebruikt 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 aanproductom in plaats daarvan verouderde rijen te markeren ("soft-delete"). -
Alle kolommen in dit basisvoorbeeld worden uiteindelijk
NOT NULL, dusNULLwaarden zijn niet toegestaan. (Ja, allemaal kolommen - primaire sleutelkolommen zijn gedefinieerdUNIQUE NOT NULLautomatisch.) Dat komt omdatNULLwaarden zouden in geen van de kolommen logisch zijn. Het maakt het leven van een beginner gemakkelijker. Maar je zult niet zo gemakkelijk wegkomen, je moetNULLbegrijpen toch behandelen. Extra kolommen kunnenNULLallow toestaan waarden, functies en joins kunnenNULLintroduce introduceren waarden in zoekopdrachten enz. -
Lees het hoofdstuk over
CREATE TABLEin 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_productis op(bill_id, product_id)in mijn voorbeeld wil je misschien nog een index toevoegen op slechtsproduct_idof(product_id, bill_id)als je vragen hebt op zoek naar een gegevenproduct_iden 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.