Làm thế nào để có mối quan hệ một-nhiều với một đứa trẻ đặc quyền?


22

Tôi muốn có một mối quan hệ một-nhiều trong đó đối với mỗi phụ huynh, một hoặc không có con cái được đánh dấu là một mục yêu thích của người Hồi giáo. Tuy nhiên, không phải cha mẹ nào cũng sẽ có một đứa con. (Hãy nghĩ về cha mẹ như các câu hỏi trên trang web này, trẻ em là câu trả lời và yêu thích là câu trả lời được chấp nhận.) Ví dụ:

TableA
    Id            INT PRIMARY KEY

TableB
    Id            INT PRIMARY KEY
    Parent        INT NOT NULL FOREIGN KEY REFERENCES TableA.Id

Theo cách tôi thấy, tôi có thể thêm cột sau vào TableA:

    FavoriteChild INT NULL FOREIGN KEY REFERENCES TableB.Id

hoặc cột sau vào BảngB:

    IsFavorite    BIT NOT NULL

Vấn đề với cách tiếp cận đầu tiên là nó giới thiệu một khóa ngoại không thể rỗng, mà theo tôi hiểu là nó không ở dạng chuẩn hóa. Vấn đề với cách tiếp cận thứ hai là cần phải thực hiện nhiều công việc hơn để đảm bảo rằng nhiều nhất một đứa trẻ là yêu thích.

Tôi nên sử dụng loại tiêu chí nào để xác định phương pháp nào sẽ sử dụng? Hoặc, có những cách tiếp cận khác mà tôi không xem xét?

Tôi đang sử dụng SQL Server 2012.

Câu trả lời:


19

Một cách khác (không có Null và không có chu kỳ trong các FOREIGN KEYmối quan hệ) là có một bảng thứ ba để lưu trữ "những đứa trẻ yêu thích". Trong hầu hết các DBMS, bạn sẽ cần một UNIQUEràng buộc bổ sung TableB.

@Aaron đã nhanh hơn để xác định rằng quy ước đặt tên ở trên khá cồng kềnh và có thể dẫn đến lỗi. Nó thường tốt hơn (và sẽ giữ cho bạn lành mạnh) nếu bạn không có Idcác cột trên tất cả các bảng của mình và nếu các cột (được nối) có cùng tên trong nhiều bảng xuất hiện. Vì vậy, đây là một đổi tên:

Parent
    ParentID        INT NOT NULL PRIMARY KEY

Child
    ChildID         INT NOT NULL PRIMARY KEY
    ParentID        INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
    UNIQUE (ParentID, ChildID)

FavoriteChild
    ParentID        INT NOT NULL PRIMARY KEY
    ChildID         INT NOT NULL 
    FOREIGN KEY (ParentID, ChildID) 
        REFERENCES Child (ParentID, ChildID)

Trong SQL-Server (mà bạn đang sử dụng), bạn cũng có tùy chọn IsFavoritecột bit mà bạn đề cập. Đứa trẻ yêu thích duy nhất của mỗi phụ huynh có thể được thực hiện thông qua Chỉ mục duy nhất được lọc:

Parent
    ParentID        INT NOT NULL PRIMARY KEY

Child
    ChildID         INT NOT NULL PRIMARY KEY
    ParentID        INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
    IsFavorite      BIT NOT NULL

CREATE UNIQUE INDEX is_FavoriteChild
  ON Child (ParentID)
  WHERE IsFavorite = 1 ;

Và lý do chính mà tùy chọn 1 của bạn không được khuyến nghị, ít nhất là không có trong SQL-Server, là do mô hình của các đường dẫn tròn trong các tham chiếu khóa ngoài có một số vấn đề.

Đọc một bài viết khá cũ: SQL theo thiết kế: Tham chiếu thông tư

Khi chèn hoặc xóa các hàng từ hai bảng, bạn sẽ gặp phải vấn đề "gà và trứng". Tôi nên chèn bảng nào trước - không vi phạm bất kỳ ràng buộc nào?

Để giải quyết điều đó, bạn phải xác định ít nhất một cột nullable. (OK, về mặt kỹ thuật bạn không cần phải có, bạn có thể có tất cả các cột như NOT NULLnhưng chỉ trong DBMS, như Postgres và Oracle, đã triển khai các ràng buộc có thể bảo vệ được. Xem câu trả lời của @ Erwin trong một câu hỏi tương tự: Ràng buộc khóa ngoài phức tạp trong SQLAlchemy về cách điều này có thể được thực hiện trong Postgres). Tuy nhiên, thiết lập này cho cảm giác như trượt trên băng mỏng.

Kiểm tra một câu hỏi gần như giống hệt nhau tại SO (nhưng đối với MySQL) Trong SQL, hai bảng có liên quan đến nhau không? trong đó câu trả lời của tôi là khá nhiều như nhau. MySQL không có chỉ mục một phần nào, vì vậy các tùy chọn khả thi duy nhất là FK nullable và giải pháp bảng bổ sung.


9

Nó phụ thuộc vào ưu tiên của bạn là gì. Bạn có muốn tránh làm việc hoặc bạn muốn tuân thủ các quy tắc nghiêm ngặt nhất của bình thường hóa?

Cá nhân, tôi nghĩ rằng tốt hơn là có IsFavoritetrong bảng con, và sẽ sẵn sàng đưa vào công việc để đảm bảo rằng nhiều nhất một đứa trẻ cho mỗi cha mẹ là yêu thích của cha mẹ. Lý do chính không liên quan gì đến khóa ngoại không có giá trị: Tôi không thích ý tưởng này ở tất cả các khóa ngoại được chỉ theo cả hai hướng.

Đề nghị của @ ypercube cũng là một sự thỏa hiệp tốt.

Xin vui lòng, xin vui lòng, xin vui lòng, không xả rác lược đồ của bạn với các tên cột vô nghĩa như thế nào Id. Tôi rất muốn nhìn thấy Idtên được đặt theo một cách có ý nghĩa trong suốt lược đồ. Liệu nó có xác định một tác giả? Ok, gọi nó đi AuthorID. Nó đại diện cho một sản phẩm? Ok, ProductID. Đây có phải là một nhân viên, và trong một số trường hợp tham khảo một người quản lý? Ok, EmployeeIDManagerIDcó ý nghĩa với tôi hơn IDParent. Mặc dù có vẻ hợp lý khi bỏ nó đi (và không cần thiết để đặt nó vào), khi bạn bắt đầu viết các phép nối phức tạp (hoặc truy vấn bài đăng ở đây), bạn chắc chắn sẽ cảm thấy một số lời chửi rủa khi bạn cố gắng đảo ngược một loạt các liên kết đó đến các cột như a.Parent = b.ID... blecch.


1

Dữ liệu thuộc về bảng con. Chúng tôi giữ chính xác với một kích hoạt trên bảng để đảm bảo một và chỉ bản ghi được đánh dấu là yêu thích (hoặc trong trường hợp của chúng tôi là địa chỉ ưa thích).

Tuy nhiên, ý tưởng về một bảng riêng biệt của @ ypercube cũng là một ý tưởng tốt.

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.