Thậm chí nếu bạn có thể đặt chuỗi danh tính trên nhiều bảng, bảng nhận xét của bạn sẽ không thể tham chiếu cả hai cột trong một khóa ngoại.
Cách tốt nhất để làm điều này, về lý thuyết thiết kế cơ sở dữ liệu quan hệ, sẽ là tạo hai bảng nhận xét. Nhưng rõ ràng, bạn muốn tránh điều đó, có lẽ vì lý do tái sử dụng mã.
Cách tiếp cận thực dụng đơn giản nhất sẽ là đặt hai cột khóa ngoài trên bảng nhận xét và chỉ tạo một giá trị rỗng và cột kia không rỗng cho mỗi nhận xét.
Một cách tiếp cận khác, có thể là sự thỏa hiệp tốt nhất, chính là điều này. Bạn tham khảo câu hỏi của mình với "ID tổ chức". Vì vậy, tạo một bảng Entity! Sau đó, các tác giả và sách và nhận xét đều có thể tham khảo rằng bảng.
Edited thêm:
Philip Kelley, Ray, và (tôi nghĩ) Artic đều đề nghị sửa đổi các bảng bình luận bằng cách thêm một entity_id
, có thể tham khảo một trong hai book_id
hoặc author_id
, và một cờ của một số loại (char(1)
, tinyint
và boolean
tương ứng) cho biết loại nào trong số này đang được đề cập đến.
Đây không phải là giải pháp tốt vì nhiều lý do, cả thực dụng (bao gồm toàn vẹn dữ liệu, báo cáo, hiệu quả) và lý thuyết.
Vấn đề đầu tiên và rõ ràng nhất là vấn đề toàn vẹn dữ liệu. Một hệ thống cơ sở dữ liệu quan hệ phải luôn chịu trách nhiệm duy trì tính toàn vẹn của dữ liệu riêng của nó, và có những cách tự nhiên và ưu tiên mà DB được thiết kế để làm điều này. Một trong những cơ chế quan trọng nhất là hệ thống khóa ngoại. Nếu cột comment.entity_id
là tham chiếu cả hai book.book_id
và author.author_id
thì không thể tạo khóa ngoại cho cột này.Chắc chắn, bạn có thể đặt một kiểm tra trong các thủ tục lưu trữ DML của bạn (chèn, cập nhật, xóa) để xác minh các tham chiếu, nhưng điều đó sẽ nhanh chóng trở thành một mớ hỗn độn lớn, vì tất cả các hoạt động DML trên cả ba bảng sẽ được tham gia.
Và điều đó dẫn chúng ta đến vấn đề hiệu quả. Bất cứ khi nào truy vấn chạy trên bảng comment
, nó sẽ yêu cầu tham gia vào bảng author
hoặc book
hoặc cả hai. Hệ thống tạo kế hoạch truy vấn sẽ không có sẵn các khóa ngoại để tối ưu hóa, vì vậy hiệu năng của nó có thể bị suy giảm rất tốt.
Sau đó, có vấn đề với lược đồ này trong báo cáo. Bất kỳ hệ thống tạo báo cáo nào cũng sẽ gặp sự cố với loại hệ thống này. Chắc chắn đây sẽ không phải là vấn đề đối với các lập trình viên chuyên gia, nhưng bất kỳ báo cáo đặc biệt nào của người dùng sẽ phải giả lập logic đằng sau khi event_id
có nghĩa là điều này hoặc điều đó, và nó có thể là một vấn đề khá xấu. Có thể bạn sẽ không bao giờ sử dụng các công cụ tạo báo cáo trên cơ sở dữ liệu này. Nhưng sau đó một lần nữa, không ai biết nơi mà một cơ sở dữ liệu sẽ được sử dụng cuối cùng. Tại sao không làm việc với hệ thống để cho phép bất cứ điều gì?
Và điều đó dẫn chúng ta đến các vấn đề lý thuyết.
Trong lý thuyết cơ sở dữ liệu quan hệ, mỗi hàng (a.k.a. "tuple") trong mỗi bảng ("biến quan hệ") đại diện cho một mệnh đề về thế giới thực. Thiết kế một bảng là quyết định hình thức của đề xuất đó. Hãy xem xét một vài ví dụ về cách thức hoạt động của nó.
comment (comment_id int, comment_type char(1), entity_id int,
user_id int, comment_text nvarchar(max), comment_date datetime)
/* comment_id identifies a comment (comment_text) that a user (user_id)
has made about a book (entity_id if comment_type = 'B') or author
(entity_id if comment_type = 'A') at a particular date and
time (comment_date).*/
Ở đây rõ ràng rằng cột (hoặc "thuộc tính") được gọi là entity_id
đang thực hiện nhiệm vụ kép. Nó không thực sự đại diện cho bất cứ điều gì, ngoại trừ với tham chiếu đến cột khác. Điều này là hoàn toàn khả thi, nhưng không đạt yêu cầu.
comment (comment_id int, book_id int, author_id int, user_id int,
comment_text nvarchar(max), comment_date datetime)
/* comment_id identifies a comment (comment_text) that a user (user_id)
has made about a book (book_id if not null) or author (author_id if
not null) at a particular date and time (comment_date). */
Điều này sẽ mua cho chúng tôi khóa ngoại là sự thiếu sót lớn nhất từ phiên bản đầu tiên. Nhưng điều này vẫn không đủ khả quan, trừ khi một bình luận duy nhất có thể đề cập đến cả một cuốn sách và một tác giả (có thể là hợp lý). Các cột dễ vỡ là một dấu hiệu cảnh báo rằng có điều gì đó không ổn với thiết kế và cũng có thể là trường hợp ở đây. Một ràng buộc kiểm tra có thể là cần thiết để tránh một nhận xét không liên quan đến gì cả, hoặc cả sách và tác giả nếu điều đó không được phép.
Từ góc độ lý thuyết (và do đó, quan điểm của tôi :)) có một lựa chọn rõ ràng nhất:
book_comment (book_comment_id int, book_id int, user_id int,
comment_text nvarchar(max), comment_date datetime)
/* book_comment_id identifies a comment (comment_text) that a
user (user_id) has made about a book (book_id) at a particular
date and time (comment_date). */
author_comment (author_comment_id int, author_id int, user_id int,
comment_text nvarchar(max), comment_date datetime)
/* author_comment_id identifies a comment (comment_text) that a
user (user_id) has made about an author (author_id) at a particular
date and time (comment_date). */
này Tùy chọn cuối cùng sẽ cung cấp hiệu suất tốt nhất, toàn vẹn dữ liệu, và dễ báo cáo. Và chi phí duy nhất là các thủ tục được lưu trữ của DML sẽ cần đưa các nhận xét vào các bảng bên phải, đó không phải là một vấn đề lớn, bởi vì họ phải biết những gì các bình luận đang đề cập đến.
Nếu kế hoạch của bạn là lấy lại tất cả các nhận xét cho một cuốn sách hoặc tác giả cùng một lúc, bạn có thể dễ dàng tạo ra một khung nhìn phía trên các bảng này sao chép các thiết kế khác, nếu đó là những gì bạn muốn làm.
create view comments as
select
book_comment_id as comment_id,
book_id as entity_id,
comment_text,
'B' as comment_type
from book_comment
union
select
author_comment_id as comment_id,
author_id as entity_id,
comment_text,
'A' as comment_type
from author_comment
Bạn không thể có một điểm khóa ngoài duy nhất cho hai bảng khác nhau, ngay cả với cờ tiện dụng. –
Đề xuất của bạn ("Tôi có thể thêm cột như" CommentorType "vào Nhận xét") là đường dẫn tôi đã đi trước khi quyết định đăng và đảm bảo không có cách nào dễ dàng hơn để thực hiện. Cảm ơn. – johnnycakes
Nhưng thêm loại bình luận vào các bình luận không phải là một giải pháp tốt! Đừng làm thế! Bạn sẽ hối tiếc! OK, bây giờ tôi cảm thấy khá hơn. Tiếp tục đi. –