2013-10-24 30 views
7

Tôi đã gặp phải tình huống này nhiều lần trong các dự án khác nhau gần đây. Dưới đây là một sơ đồ của bốn bảng, dán nhãn với các chữ cái:Bốn thiết kế mối quan hệ bảng

 A 
    1/\ 1 
/ \ 
*/ \ * 
B  C 
1 \ /1 
    \ /
    * \/* 
    D 

Trong kịch bản này, nó có thể cho các dữ liệu trở nên không phù hợp nếu các phím B-AC-A không phù hợp cho một được cung cấp D.

Đối với một cụ thể (tạo thành) Ví dụ, hãy tưởng tượng ACompany, BEmployee, CProject, và DWorkItem. Trong trường hợp này, không có gì ngăn cản một mục công việc được tạo ra mà tuyên bố được gán cho một người thậm chí không làm việc cho công ty sở hữu dự án.

Tôi chủ yếu chỉ tò mò, có một giải pháp thiết kế cho sự cố này không? Tôi biết trong các ứng dụng thực tế, điều này quan trọng bạn có thể sử dụng các bộ kích hoạt hoặc một số biện pháp bảo vệ khác. Tôi đã không tìm thấy một cách để thay đổi các bảng để làm cho một sự không thống nhất như vậy không thể. Là có một cách?

Lưu ý rằng chỉ cắt đứt một trong những kết nối, như C-A không làm việc, bởi vì nếu không có D 's tồn tại cho C mà bạn sẽ không có cách nào truy tìm các kết nối trở lại A.

+0

Câu hỏi hay. Tôi đã luôn luôn đặt các biện pháp bảo vệ để ngăn chặn loại điều này trong lớp ứng dụng. Nó sẽ rất thú vị để xem những gì người khác phải nói. –

+0

Tôi sử dụng để thực thi loại hạn chế này trong quy tắc kinh doanh, chứ không phải kích hoạt. –

+0

Xác định lại các thực thể của bạn: nhân viên - >> người (nhớ: người thụ hưởng đã từng là nhân viên một lần) Thêm trục thời gian có thể là cần thiết (và một vài tham số phụ khác; tìm kiếm kích thước ẩn). – wildplasser

Trả lời

7

Sử dụng các phím tổng hợp (ví dụ: khóa bao gồm nhiều trường) cho bảng hạ lưu. Sau đó, trong D, bạn có thể sử dụng chỉ là một lĩnh vực tổ chức của một chính:

[EDIT: Cố định ngu ngốc sao & dán lỗi trong D's 2 FK]

CREATE TABLE A (
    A_ID INTEGER PRIMARY KEY 
    -- Any other fields you want... 
); 

CREATE TABLE B (
    A_ID INTEGER REFERENCES A.A_ID, 
    B_ID INTEGER, 
    -- Any other fields you want... 
    PRIMARY KEY (A_ID, B_ID) 
); 

CREATE TABLE C (
    A_ID INTEGER REFERENCES A.A_ID, 
    C_ID INTEGER, 
    -- Any other fields you want... 
    PRIMARY KEY (A_ID, C_ID) 
); 

CREATE TABLE D (
    A_ID INTEGER, -- This field forms part of the FK for BOTH B and C 
    B_ID INTEGER, 
    C_ID INTEGER, 
    D_ID INTEGER, 
    -- Any other fields you want... 
    PRIMARY KEY (A_ID, B_ID, C_ID, D_ID), 
    FOREIGN KEY (A_ID, B_ID) REFERENCES B (A_ID, B_ID), 
    FOREIGN KEY (A_ID, C_ID) REFERENCES C (A_ID, C_ID) 
); 

tôi đã không kiểm tra trên SQL, nhưng bạn có được ý tưởng hy vọng. Lưu ý rằng D không cần một ràng buộc FK thứ ba trở lại A, bởi vì điều này đã được ngụ ý bởi các FK khác của nó (trên thực tế nó được ngụ ý riêng biệt bởi mỗi một trong số chúng).

Kiểm tra tính toàn vẹn tham chiếu luôn tốt hơn trình kích hoạt - ít nhất với PostgreSQL, và tôi nghi ngờ điều đó đúng với tất cả RDBMS.

+0

Tôi tin rằng đây là cách duy nhất, nhưng là một địa ngục phát triển (các thực thể khác có thể yêu cầu các phím dài hơn). Hiệu suất có thể sẽ rất khủng khiếp. –

+1

@LluisMartinez: Tôi hy vọng sẽ nhanh hơn khi sử dụng các số nguyên đơn (hoặc có thể GUID), nhưng tôi chưa gặp vấn đề về hiệu năng với các bảng được tổ chức theo cách này trên PostgreSQL, một số trong đó có hàng triệu hàng. Trên thực tế tôi thấy nó làm cho việc gỡ lỗi dễ dàng hơn nhiều - khi làm việc với một bản ghi từ một số bảng phụ thuộc, PK của bạn ngay lập tức cho bạn biết rất nhiều về nó, thay vì chỉ là một mã thông báo mờ đục. Thứ hai, bạn thường có thể sắp xếp hoặc nhóm theo các trường FK này mà không cần tham gia vào (các) bảng cha. –

Các vấn đề liên quan