Làm thế nào để triển khai mối quan hệ nhiều-nhiều trong PostgreSQL?


95

Tôi tin rằng tiêu đề là tự giải thích. Làm cách nào để bạn tạo cấu trúc bảng trong PostgreSQL để tạo mối quan hệ nhiều-nhiều.

Ví dụ của tôi:

Product(name, price);
Bill(name, date, Products);

2
xóa sản phẩm khỏi bảng hóa đơn, tạo một bảng mới có tên "bill_products" với hai trường: một trường trỏ vào sản phẩm, một trường trỏ vào hóa đơn. biến hai trường đó thành khóa chính của bảng mới.
Marc B

So bill_products (hóa đơn, sản phẩm); ? Và cả hai người họ PK?
Radu Gheorghiu

1
vâng. họ sẽ là một FK riêng lẻ chỉ vào các bảng tương ứng của họ, và cùng nhau họ sẽ là PK cho bảng mới.
Marc B

Vậy, bill_product (product tham chiếu product.name, bill tham chiếu bill.name, (product, bill) khóa chính)?
Radu Gheorghiu

Họ sẽ trỏ đến các trường PK của bảng Sản phẩm và Bảng hóa đơn.
Marc B

Câu trả lời:


298

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:

  • Các n: mối quan hệ m thường được thực hiện bởi một bảng riêng biệt - bill_producttrong trường hợp này.

  • Tôi đã thêm serialcác cột làm khóa chính thay thế . Trong Postgres 10 trở lên, hãy xem xét một IDENTITYcột thay thế. Xem:

    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 4 byte integer(hoặc thậm chí 8 byte bigint) so với chuỗi được lưu trữ dưới dạng texthoặc varchar.

  • Không sử dụng tên của các kiểu dữ liệu cơ bản datenhư 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 các từ nhận dạng chữ hoachữ 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 productthành product( product_namehoặc tương tự). Đó là một 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 - điều mà bạn thực hiện rất nhiều trong cơ sở dữ liệu quan hệ - bạn sẽ có nhiều cột có tên "name" 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ủa a billsẽ là gì. bill_idcó lẽ sẽ đủ trong trường hợp này.

  • pricekiểu dữ liệu numeric để 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ỉ giải quyết các số nguyên, hãy làm điều đó integer. Ví dụ: bạn có thể tiết kiệm giá dưới dạng Cents .

  • Các amount( "Products"trong câu hỏi của bạn) đi vào bảng liên kết bill_productvà là loại numericlà tốt. Một lần nữa, integernếu bạn đối phó với các số nguyên.

  • Bạn thấy các khóa ngoại trong bill_product? Tôi tạo ra cả những thay đổi cascade: ON UPDATE CASCADE. Nếu một product_idhoặc bill_idnên thay đổi, thay đổi sẽ được xếp theo tầng cho tất cả các mục nhập phụ thuộc vào bill_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ụng ON DELETE CASCADEcho bill_id: Nếu một hóa đơn bị xóa, các chi tiết của nó sẽ chết cùng với nó.
    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. productThay vào đó, bạn sẽ thêm một cột khác vào để đánh dấu các hàng lỗi thời ("soft-delete").

  • Tất cả các cột trong ví dụ cơ bản này kết thúc bằng NOT NULL, vì vậy NULLgiá trị không được phép. (Có, tất cả các cột - cột khóa chính được xác định UNIQUE NOT NULLtự động.) Đó là 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, dù sao bạn cũng cần hiểu cách NULLxử lý . Các cột bổ sung có thể cho phép NULLcác giá trị, các hàm và phép nối có thể giới thiệu NULLcác giá trị trong các truy vấn, v.v.

  • Đọc chương trên 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 chính, điều này giúp cho các truy vấn với các điều kiện trên (các) cột PK 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. Vì PK bill_productđược bật (bill_id, product_id)trong ví dụ của tôi, bạn có thể muốn thêm một chỉ mục khác vào chỉ product_idhoặc (product_id, bill_id)nếu bạn có truy vấn tìm kiếm cho sẵn product_idvà không bill_id. Xem:

  • Đọc chương về chỉ mục trong sách hướng dẫn .


Làm cách nào để tạo chỉ mục cho bảng ánh xạ bill_product? Thông thường nó vẻ nên thích: CREATE INDEX idx_bill_product_id ON booked_rates(bill_id, product_id). Thê nay đung không?
codyLine

1
@codyLine: Chỉ số này được PK tự động tạo.
Erwin Brandstetter

1
@ErwinBrandstetter: Bạn không nên tạo chỉ mục trên bill_product cho cột product_id?
Christian

2
@ ChristianB.Almeida: Đúng là hữu ích trong nhiều trường hợp. Tôi đã thêm một chút về lập chỉ mục.
Erwin Brandstetter

1
@Jakov: Chỉ có 1 hàng cho mỗi hóa đơn trong bảng bill. Chúng tôi cần số lượng cho mỗi mục được thêm vào bill_product.
Erwin Brandstetter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.