Các câu lệnh SQL DDL (ngôn ngữ định nghĩa dữ liệu) có thể trông giống như sau:
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
);
Tôi đã thực hiện một vài điều chỉnh:
-
Mối quan hệ n:m thường được triển khai bởi một bảng riêng -
bill_product
trong trường hợp này. -
Tôi đã thêm
serial
cột làm khóa chính thay thế . Trong Postgres 10 trở lên, hãy xem xét mộtIDENTITY
thay vào đó. Xem:- Đổi tên bảng một cách an toàn bằng cách sử dụng các cột khóa chính nối tiếp
- Cột bảng tăng dần tự động
- https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/
Tôi thực sự khuyên bạn điều đó, bởi vì tên của một sản phẩm hầu như không phải là duy nhất (không phải là "khóa tự nhiên" tốt). Ngoài ra, việc thực thi tính duy nhất và tham chiếu cột trong khóa ngoại thường rẻ hơn với số nguyên
integer
(hoặc thậm chí làbigint
8 byte ) so với một chuỗi được lưu trữ dưới dạngtext
hoặcvarchar
. -
Không sử dụng tên của các kiểu dữ liệu cơ bản như
date
dưới dạng số nhận dạng . Mặc dù điều này là có thể, nhưng nó là một phong cách tồi và dẫn đến các lỗi và thông báo lỗi khó hiểu. Sử dụng số nhận dạng hợp pháp, chữ thường, không được trích dẫn. Không bao giờ sử dụng các từ dành riêng và tránh sử dụng các từ phân biệt chữ hoa và chữ thường được trích dẫn kép nếu bạn có thể. -
"name" không phải là một cái tên hay. Tôi đã đổi tên cột của bảng
product
trở thànhproduct
(hoặcproduct_name
hoặc tương tự). Đó là quy ước đặt tên tốt hơn . Mặt khác, khi bạn nối một vài bảng trong một truy vấn - bạn thực hiện rất nhiều trong cơ sở dữ liệu quan hệ - bạn kết thúc với nhiều cột có tên là "tên" và phải sử dụng bí danh cột để sắp xếp lộn xộn. Điều đó không hữu ích. Một mẫu chống phổ biến khác sẽ chỉ là "id" làm tên cột.
Tôi không chắc tên củabill
sẽ được.bill_id
có lẽ sẽ đủ trong trường hợp này. -
price
thuộc loại dữ liệunumeric
để lưu trữ các số phân số chính xác như đã nhập (kiểu chính xác tùy ý thay vì kiểu dấu phẩy động). Nếu bạn chỉ xử lý các số nguyên, hãy đặtinteger
đó . Ví dụ:bạn có thể tiết kiệm giá dưới dạng Cents . -
Số tiền
amount
("Products"
trong câu hỏi của bạn) đi vào bảng liên kếtbill_product
và thuộc loạinumeric
cũng. Xin nhắc lại,integer
nếu bạn chỉ giải quyết các số nguyên. -
Bạn thấy khóa ngoại trong
bill_product
? Tôi đã tạo cả hai để phân tầng các thay đổi:ON UPDATE CASCADE
. Nếu mộtproduct_id
hoặcbill_id
nên thay đổi, thay đổi được tính theo tầng cho tất cả các mục nhập tùy thuộc vàobill_product
và không có gì phá vỡ. Đó chỉ là những tài liệu tham khảo không có ý nghĩa riêng.
Tôi cũng đã sử dụngON DELETE CASCADE
chobill_id
:Nếu hóa đơn bị xóa, các chi tiết của hóa đơn đó sẽ chết theo.
Không phải như vậy đối với sản phẩm:Bạn không muốn xóa sản phẩm được sử dụng trong hóa đơn. Postgres sẽ báo lỗi nếu bạn cố gắng làm điều này. Bạn sẽ thêm một cột khác vàoproduct
để đánh dấu các hàng lỗi thời ("soft-delete") để thay thế. -
Tất cả các cột trong ví dụ cơ bản này kết thúc là
NOT NULL
, vì vậyNULL
các giá trị không được phép. (Có, tất cả cột - cột khóa chính được xác địnhUNIQUE NOT NULL
tự động.) Đó là bởi vìNULL
các giá trị sẽ không có ý nghĩa trong bất kỳ cột nào. Nó làm cho cuộc sống của người mới bắt đầu dễ dàng hơn. Nhưng bạn sẽ không thoát khỏi dễ dàng như vậy, bạn cần hiểuNULL
xử lý bằng cách nào. Các cột bổ sung có thể cho phépNULL
các giá trị, hàm và phép nối có thể giới thiệuNULL
giá trị trong truy vấn, v.v. -
Đọc chương về
CREATE TABLE
trong sách hướng dẫn. -
Khóa chính được triển khai với một chỉ mục duy nhất trên các cột quan trọng, điều này làm cho các truy vấn với các điều kiện trên (các) cột PK trở nên nhanh chóng. Tuy nhiên, trình tự của các cột chính có liên quan trong các khóa nhiều cột. Kể từ khi PK trên
bill_product
nằm trên(bill_id, product_id)
trong ví dụ của tôi, bạn có thể chỉ muốn thêm một chỉ mục khác trênproduct_id
hoặc(product_id, bill_id)
nếu bạn có truy vấn tìm kiếm mộtproduct_id
nhất định và không cóbill_id
. Xem:- Khóa chính tổng hợp PostgreSQL
- Chỉ số tổng hợp có tốt cho các truy vấn trên trường đầu tiên không?
- Hoạt động của các chỉ mục trong PostgreSQL
-
Đọc chương về chỉ mục trong sách hướng dẫn.