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_producttrong trường hợp này. -
Tôi đã thêm
serialcột làm khóa chính thay thế . Trong Postgres 10 trở lên, hãy xem xét mộtIDENTITYthay 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àbigint8 byte ) so với một chuỗi được lưu trữ dưới dạngtexthoặcvarchar. -
Không sử dụng tên của các kiểu dữ liệu cơ bản như
datedướ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
producttrở thànhproduct(hoặcproduct_namehoặ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ủabillsẽ được.bill_idcó lẽ sẽ đủ trong trường hợp này. -
pricethuộ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_productvà thuộc loạinumericcũng. Xin nhắc lại,integernế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_idhoặcbill_idnê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_productvà 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 CASCADEchobill_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ậyNULLcá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 NULLtự động.) Đó là bởi vìNULLcá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ểuNULLxử lý bằng cách nào. Các cột bổ sung có thể cho phépNULLcác giá trị, hàm và phép nối có thể giới thiệuNULLgiá trị trong truy vấn, v.v. -
Đọc chương về
CREATE TABLEtrong 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_productnằ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_idhoặc(product_id, bill_id)nếu bạn có truy vấn tìm kiếm mộtproduct_idnhấ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.