Thiết kế cơ sở dữ liệu: Bình thường hóa mối quan hệ giữa nhiều người (nhiều-nhiều))


13

Phiên bản ngắn

Tôi phải thêm một số thuộc tính bổ sung cố định cho mỗi cặp trong một liên kết nhiều-nhiều hiện có. Bỏ qua các sơ đồ bên dưới, tùy chọn 1-4 nào là cách tốt nhất, về mặt lợi thế và bất lợi, để thực hiện điều này bằng cách mở rộng Trường hợp cơ sở? Hoặc, có một sự thay thế tốt hơn mà tôi chưa xem xét ở đây?

Phiên bản dài hơn

Tôi hiện có hai bảng trong mối quan hệ nhiều-nhiều, thông qua bảng tham gia trung gian. Bây giờ tôi cần thêm các liên kết bổ sung vào các thuộc tính thuộc về cặp đối tượng hiện có. Tôi có một số lượng cố định các thuộc tính này cho mỗi cặp, mặc dù một mục trong bảng thuộc tính có thể áp dụng cho nhiều cặp (hoặc thậm chí được sử dụng nhiều lần cho một cặp). Tôi đang cố gắng xác định cách tốt nhất để làm điều này và đang gặp khó khăn trong việc tìm ra cách nghĩ về tình huống. Về mặt ngữ nghĩa, dường như tôi có thể mô tả nó như bất kỳ điều nào sau đây cũng tốt như sau:

  1. Một cặp được liên kết với một tập hợp các thuộc tính bổ sung cố định
  2. Một cặp được liên kết với nhiều thuộc tính bổ sung
  3. Nhiều (hai) đối tượng được liên kết với một tập các thuộc tính
  4. Nhiều đối tượng liên kết với nhiều thuộc tính

Thí dụ

Tôi có hai loại đối tượng, X và Y, mỗi loại có ID duy nhất và bảng liên kết objx_objyvới các cột x_idy_idcùng nhau tạo thành khóa chính cho liên kết. Mỗi X có thể liên quan đến nhiều Y và ngược lại. Đây là thiết lập cho mối quan hệ nhiều-nhiều hiện có của tôi.

Trường hợp cơ sở

Trường hợp cơ sở

Ngoài ra, tôi có một tập các thuộc tính được xác định trong một bảng khác và một tập hợp các điều kiện theo đó một cặp (X, Y) nhất định sẽ có thuộc tính P. Số lượng điều kiện được cố định và giống nhau cho tất cả các cặp. Về cơ bản, họ nói "Trong tình huống C1, cặp (X1, Y1) có thuộc tính P1", "Trong tình huống C2, cặp (X1, Y1) có thuộc tính P2", v.v., trong ba tình huống / điều kiện cho mỗi cặp tham gia bàn.

lựa chọn 1

Trong tình hình hiện tại của tôi có đúng ba điều kiện như vậy, và tôi không có lý do để hy vọng rằng sẽ tăng, vì vậy một khả năng là để thêm các cột c1_p_id, c2_p_idc3_p_idđể featx_featyquy định cụ thể cho một cho x_idy_id, trong đó tài sản p_idđể sử dụng trong mỗi trong ba trường hợp .

lựa chọn 1

Đây dường như không phải là một ý tưởng tuyệt vời đối với tôi, vì nó làm phức tạp SQL để chọn tất cả các thuộc tính được áp dụng cho một tính năng và không dễ dàng mở rộng quy mô sang nhiều điều kiện hơn. Tuy nhiên, nó thực thi yêu cầu của một số điều kiện nhất định cho mỗi cặp (X, Y). Trong thực tế, nó là lựa chọn duy nhất ở đây làm như vậy.

Lựa chọn 2

Tạo bảng điều kiện condvà thêm ID điều kiện vào khóa chính của bảng tham gia.

Lựa chọn 2

Một nhược điểm của điều này là nó không chỉ định số lượng điều kiện cho mỗi cặp. Một điều nữa là khi tôi chỉ xem xét mối quan hệ ban đầu, với một vài thứ như

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

Sau đó tôi phải thêm một DISTINCTmệnh đề để tránh các mục trùng lặp. Điều này dường như đã mất thực tế rằng mỗi cặp chỉ nên tồn tại một lần.

Lựa chọn 3

Tạo một 'cặp ID' mới trong bảng tham gia và sau đó có một bảng liên kết thứ hai giữa cái đầu tiên và các thuộc tính và điều kiện.

Lựa chọn 3

Điều này dường như có ít nhược điểm nhất, ngoài việc không thực thi một số điều kiện cố định cho mỗi cặp. Liệu nó có ý nghĩa mặc dù để tạo một ID mới mà không xác định được gì ngoài các ID hiện có?

Tùy chọn 4 (3b)

Về cơ bản giống như Tùy chọn 3, nhưng không tạo ra trường ID bổ sung. Điều này được thực hiện bằng cách đặt cả ID gốc vào bảng tham gia mới, vì vậy nó chứa x_idy_idcác trường, thay vì xy_id.

Lựa chọn 4

Một lợi thế bổ sung cho hình thức này là nó không làm thay đổi các bảng hiện có (mặc dù chúng chưa được sản xuất). Tuy nhiên, về cơ bản, nó sao chép toàn bộ một bảng nhiều lần (hoặc cảm thấy như vậy, dù sao đi nữa) nên cũng không có vẻ lý tưởng.

Tóm lược

Cảm giác của tôi là Tùy chọn 3 và 4 tương tự nhau đủ để tôi có thể đi với một trong hai. Bây giờ tôi có thể sẽ có nếu không yêu cầu một số lượng nhỏ các liên kết cố định đến các thuộc tính, điều này làm cho Phương án 1 có vẻ hợp lý hơn so với cách khác. Dựa trên một số thử nghiệm rất hạn chế, việc thêm một DISTINCTmệnh đề vào các truy vấn của tôi dường như không ảnh hưởng đến hiệu suất trong tình huống này, nhưng tôi không chắc rằng Tùy chọn 2 đại diện cho tình huống cũng như các vấn đề khác, vì sự trùng lặp cố hữu gây ra bởi việc đặt các cặp (X, Y) giống nhau trong nhiều hàng của bảng liên kết.

Là một trong những lựa chọn này theo cách tốt nhất của tôi về phía trước, hoặc có một cấu trúc khác tôi nên xem xét?


Nhìn chung, 1 và 4 có vẻ là lựa chọn tốt nhất, tôi đồng ý. Sẽ không dễ để thực thi số lượng (3) thuộc tính cố định với tùy chọn 4 nhưng tôi nghĩ nó khả thi.
ypercubeᵀᴹ

Đối với DISTINCTmệnh đề, tôi đã nghĩ đến một truy vấn giống như truy vấn ở cuối # 2, liên kết xyxuyên suốt xycnhưng không đề cập đến c... Vì vậy, nếu tôi bị (x_id, y_id, c_id)ràng buộc UNIQUEvới các hàng (1,1,1)(1,1,2), sau đó SELECT x.id, y.id FROM x JOIN xyc JOIN y, tôi sẽ nhận lại hai truy vấn giống hệt nhau hàng (1,1), và (1,1).
Michael Underwood

1
À được rồi Tôi sẽ bỏ qua tùy chọn 2 nào. Tôi sẽ đi với 1 hoặc 4.
ypercubeᵀᴹ

Càng nghĩ về nó, tôi càng cảm thấy việc hạn chế số lượng tài sản thành chính xác ba là yêu cầu ít quan trọng nhất của tôi. Vì vậy, việc chặn các phản hồi mang tính xây dựng bổ sung trong thời gian tới, có lẽ tôi sẽ đi với số 4 vào thời điểm này. Cảm ơn cho đầu vào của bạn, @ypercube!
Michael Underwood

Câu trả lời:


7
  • lựa chọn 1

    * Điều này có vẻ không phải là một ý tưởng tuyệt vời đối với tôi, vì nó làm phức tạp SQL để chọn tất cả các thuộc tính được áp dụng cho một tính năng.

    Nó không nhất thiết làm phức tạp SQL truy vấn (xem kết luận bên dưới).

    Không và dễ dàng mở rộng quy mô sang nhiều điều kiện hơn

    Nó dễ dàng thay đổi theo nhiều điều kiện, miễn là vẫn còn một số điều kiện cố định và không có hàng chục hoặc hàng trăm.

    Tuy nhiên, nó thực thi yêu cầu của một số điều kiện nhất định cho mỗi cặp (X, Y). Trên thực tế, đây là lựa chọn duy nhất ở đây làm như vậy. *

    Điều đó đúng, và mặc dù bạn nói trong một bình luận rằng đây là "yêu cầu ít quan trọng nhất của tôi", bạn chưa nói nó không quan trọng.

  • Lựa chọn 2

    Một nhược điểm của điều này là nó không chỉ định số lượng điều kiện cho mỗi cặp. Một điều nữa là khi tôi chỉ xem xét mối quan hệ ban đầu thì tôi phải thêm một mệnh đề DISTINCT để tránh các mục trùng lặp.

    Tôi nghĩ rằng bạn có thể bỏ qua tùy chọn này vì các biến chứng bạn đề cập. Các objx_objybảng có khả năng là bảng lái xe cho một số thắc mắc của bạn (ví dụ: "chọn tất cả các đặc tính áp dụng cho một tính năng", mà tôi đang tham gia để có nghĩa là tất cả các đặc tính áp dụng cho một objxhoặc objy). Bạn có thể sử dụng chế độ xem để áp dụng trước, DISTINCTvì vậy đây không phải là vấn đề phức tạp đối với các truy vấn, nhưng điều đó sẽ mở rộng rất kém hiệu quả về mặt hiệu suất để đạt được rất ít.

  • Lựa chọn 3

    Liệu nó có ý nghĩa mặc dù để tạo một ID mới mà không xác định được gì ngoài các ID hiện có?

    Không, nó không - Lựa chọn 4 tốt hơn về mọi mặt.

  • Lựa chọn 4

    Về cơ bản, nó sao chép toàn bộ một bảng nhiều lần (hoặc cảm thấy như vậy, dù sao đi nữa) nên cũng không có vẻ lý tưởng.

    Tùy chọn này là tốt - đó là cách rõ ràng để thiết lập các mối quan hệ nếu số lượng thuộc tính là biến hoặc có thể thay đổi

Phần kết luận

Sở thích của tôi sẽ là tùy chọn 1 nếu số lượng thuộc tính trên mỗi objx_objycó khả năng ổn định và nếu bạn không thể tưởng tượng được sẽ thêm nhiều hơn một số ít. Đây cũng là tùy chọn duy nhất thực thi ràng buộc 'số thuộc tính = 3' - thực thi một ràng buộc tương tự đối với tùy chọn 4 có thể sẽ liên quan đến việc thêm c1_p_idcác cột cột vào bảng xy *.

Nếu bạn thực sự không quan tâm nhiều đến điều kiện đó và bạn cũng có lý do để nghi ngờ rằng số lượng điều kiện thuộc tính sẽ ổn định thì hãy chọn tùy chọn 4.

Nếu bạn không chắc chắn, hãy chọn tùy chọn 1 - nó đơn giản hơn và chắc chắn sẽ tốt hơn nếu bạn có tùy chọn này, như những người khác đã nói. Nếu bạn tắt tùy chọn 1 ", vì nó làm phức tạp SQL để chọn tất cả các thuộc tính được áp dụng cho một tính năng" Tôi khuyên bạn nên tạo chế độ xem để cung cấp cùng một dữ liệu như bảng phụ trong tùy chọn 4:

tùy chọn 1 bảng:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

xem để 'mô phỏng' tùy chọn 4:

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

"Chọn tất cả các thuộc tính được áp dụng cho một tính năng":

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

dbfiddle ở đây


-3

Tôi tin rằng bất kỳ tùy chọn nào trong số này có thể hoạt động, nhưng tôi sẽ chọn tùy chọn 1 nếu số điều kiện thực sự cố định ở mức 3 và tùy chọn 2 nếu không. Dao cạo của Occam cũng hoạt động cho thiết kế cơ sở dữ liệu, tất cả các yếu tố khác đều bằng nhau, thiết kế đơn giản nhất thường là tốt nhất.

Mặc dù nếu bạn muốn tuân theo các quy tắc chuẩn hóa cơ sở dữ liệu nghiêm ngặt, tôi tin rằng bạn cần phải đi với 2 bất kể số lượng điều kiện có cố định hay không.

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.