Instrukcje SQL DDL (język definicji danych) mogą wyglądać tak:
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
);
Wprowadziłem kilka poprawek:
-
Relacja n:m jest zwykle implementowany przez oddzielną tabelę -
bill_product
w tym przypadku. -
Dodałem
serial
kolumny jako zastępcze klucze podstawowe . W Postgresie 10 lub nowszym rozważIDENTITY
zamiast kolumny. Zobacz:- Bezpiecznie zmieniaj nazwy tabel przy użyciu seryjnych kolumn klucza podstawowego
- Automatyczna inkrementacja kolumny tabeli
- https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/
Gorąco polecam, ponieważ nazwa produktu nie jest unikalna (nie jest to dobry „naturalny klucz”). Ponadto wymuszanie unikalności i odwoływanie się do kolumny w kluczach obcych jest zazwyczaj tańsze w przypadku 4-bajtowej
integer
(lub nawet 8-bajtowybigint
) niż za pomocą ciągu przechowywanego jakotext
lubvarchar
. -
Nie używaj nazw podstawowych typów danych, takich jak
date
jako identyfikatory . Chociaż jest to możliwe, jest to zły styl i prowadzi do mylących błędów i komunikatów o błędach. Używaj legalnych, małych liter, nie cytowanych identyfikatorów. Nigdy nie używaj zastrzeżonych słów i unikaj podwójnych cudzysłowów, jeśli możesz. -
„imię” nie jest dobrym imieniem. Zmieniłem nazwę kolumny tabeli
product
byćproduct
(lubproduct_name
lub podobne). To jest lepsza konwencja nazewnictwa . W przeciwnym razie, gdy łączysz kilka tabel w zapytaniu – co robisz często w relacyjnej bazie danych - kończysz z wieloma kolumnami o nazwie "nazwa" i musisz użyć aliasów kolumn, aby uporządkować bałagan. To nie jest pomocne. Innym szeroko rozpowszechnionym antywzorcem byłby po prostu „id” jako nazwa kolumny.
Nie jestem pewien, jak nazywa siębill
byłoby.bill_id
prawdopodobnie wystarczy w tym przypadku. -
price
ma typ danychnumeric
do przechowywania liczb ułamkowych dokładnie tak, jak zostały wprowadzone (dowolny typ precyzji zamiast typu zmiennoprzecinkowego). Jeśli zajmujesz się wyłącznie liczbami całkowitymi, ustawinteger
. Na przykład możesz zapisać ceny jako centy . -
amount
("Products"
w Twoim pytaniu) przechodzi do tabeli linkówbill_product
i jest typunumeric
także. Ponownie,integer
jeśli zajmujesz się wyłącznie liczbami całkowitymi. -
Widzisz klucze obce w
bill_product
? Stworzyłem oba do kaskadowych zmian:ON UPDATE CASCADE
. Jeśliproduct_id
lubbill_id
powinna się zmienić, zmiana jest kaskadowana do wszystkich zależnych wpisów wbill_product
i nic się nie psuje. To są tylko referencje, które same w sobie nie mają znaczenia.
Użyłem równieżON DELETE CASCADE
dlabill_id
:Jeśli rachunek zostanie usunięty, jego szczegóły umrą wraz z nim.
Nie dotyczy to produktów:Nie chcesz usuwać produktu, który jest użyty w rachunku. Postgres zgłosi błąd, jeśli spróbujesz tego. Dodajesz kolejną kolumnę doproduct
aby zamiast tego zaznaczyć przestarzałe wiersze ("soft-delete"). -
Wszystkie kolumny w tym podstawowym przykładzie mają wartość
NOT NULL
, więcNULL
wartości są niedozwolone. (Tak, wszystkie kolumny - kolumny klucza głównego są zdefiniowaneUNIQUE NOT NULL
automatycznie.) Dzieje się tak, ponieważNULL
wartości nie miałyby sensu w żadnej z kolumn. Ułatwia życie początkującemu. Ale nie uciekniesz tak łatwo, musisz zrozumiećNULL
mimo to obsługa. Dodatkowe kolumny mogą pozwolić naNULL
wartości, funkcje i złączenia mogą wprowadzićNULL
wartości w zapytaniach itp. -
Przeczytaj rozdział o
CREATE TABLE
w instrukcji. -
Klucze podstawowe są implementowane z unikalnym indeksem w kolumnach kluczowych, co przyspiesza zapytania z warunkami w kolumnach PK. Jednak kolejność kolumn kluczy ma znaczenie w kluczach wielokolumnowych. Od czasu PK na temat
bill_product
jest na(bill_id, product_id)
w moim przykładzie możesz chcieć dodać kolejny indeks tylko dlaproduct_id
lub(product_id, bill_id)
jeśli masz zapytania dotyczące danegoproduct_id
i bezbill_id
. Zobacz:- Złożony klucz podstawowy PostgreSQL
- Czy indeks złożony jest również odpowiedni dla zapytań w pierwszym polu?
- Praca z indeksami w PostgreSQL
-
Przeczytaj rozdział dotyczący indeksów w podręczniku.