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
-
Прочетете главата за индексите в ръководството.