SQL DDL (език за дефиниция на данни) може да изглежда така:
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
);
Направих няколко корекции:
-
Връзката n:m обикновено се изпълнява от отделна таблица -
bill_product
в този случай. -
Добавих
serial
колони като сурогатни първични ключове . В Postgres 10 или по-нова версия помислете заIDENTITY
колона вместо това. Вижте:- Безопасно преименувайте таблици с помощта на колони от сериен първичен ключ
- Колона на таблицата с автоматично увеличение
- https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/
Горещо препоръчвам това, защото името на даден продукт едва ли е уникално (не е добър "естествен ключ"). Също така, налагането на уникалност и препращане към колоната във външни ключове обикновено е по-евтино с 4-байтово
integer
(или дори 8-байтовbigint
), отколкото с низ, съхранен катоtext
илиvarchar
. -
Не използвайте имена на основни типове данни като
date
като идентификатори . Въпреки че това е възможно, това е лош стил и води до объркващи грешки и съобщения за грешки. Използвайте легални идентификатори с малки букви, които не са в кавички. Никога не използвайте запазени думи и избягвайте двойни кавички, ако можете. -
"име" не е добро име. Преименувах колоната на таблицата
product
да бъдеproduct
(илиproduct_name
или подобен). Това е по-добронаименуване . В противен случай, когато се присъедините към няколко таблици в заявка - което правите много в релационна база данни - в крайна сметка получавате множество колони с име "име" и трябва да използвате псевдоними на колони, за да разрешите бъркотията. Това не е полезно. Друг широко разпространен анти-модел би бил просто "id" като име на колона.
Не съм сигурен какво е името наbill
би било.bill_id
вероятно ще бъде достатъчно в този случай. -
price
е от тип данниnumeric
за съхраняване на дробни числа точно както са въведени (тип с произволна точност вместо тип с плаваща запетая). Ако работите изключително с цели числа, направете товаinteger
. Например, можете да запазите цени като цента . -
amount
("Products"
във вашия въпрос) отива в таблицата за свързванеbill_product
и е от типnumeric
също така. Отновоinteger
ако работите изключително с цели числа. -
Виждате външните ключове в
bill_product
? Създадох и двете за каскадна промяна:ON UPDATE CASCADE
. Акоproduct_id
илиbill_id
трябва да се промени, промяната е каскадна към всички зависими записи вbill_product
и нищо не се счупва. Това са само препратки без собствено значение.
Използвах иON DELETE CASCADE
заbill_id
:Ако сметката бъде изтрита, нейните детайли умират заедно с нея.
Не е така за продуктите:Не искате да изтриете продукт, който се използва в сметката. Postgres ще изведе грешка, ако опитате това. Бихте добавили друга колона къмproduct
вместо това да маркирате остарели редове („soft-delete“). -
Всички колони в този основен пример се оказват
NOT NULL
, така чеNULL
стойности не са разрешени. (Да, всички колони - колоните с първичен ключ са дефинираниUNIQUE NOT NULL
автоматично.) Това е защотоNULL
стойностите няма да имат смисъл в нито една от колоните. Това улеснява живота на начинаещия. Но няма да се измъкнете толкова лесно, трябва да разберетеNULL
боравене така или иначе. Допълнителните колони може да позволяватNULL
стойности, функции и съединения могат да въвеждатNULL
стойности в заявки и др. -
Прочетете главата за
CREATE TABLE
в ръководството. -
Първичните ключове се реализират с уникален индекс на ключовите колони, което прави заявките с условия в колоната(ите) на PK бързо. Въпреки това, последователността на ключовите колони е уместна в многоколонните ключове. Тъй като PK на
bill_product
е на(bill_id, product_id)
в моя пример може да искате да добавите друг индекс само къмproduct_id
или(product_id, bill_id)
ако имате заявки, търсещи даденproduct_id
и безbill_id
. Вижте:- Сложен първичен ключ на PostgreSQL
- Съставният индекс добър ли е и за заявки в първото поле?
- Работа с индекси в PostgreSQL
-
Прочетете главата за индексите в ръководството.