Bạn sẽ cần liên kết N:M giữa books
và authors
, vì một cuốn sách có thể có nhiều tác giả và mỗi tác giả có thể đã viết nhiều hơn một cuốn sách. Trong RDBMS có nghĩa là bạn sẽ cần một written_by
bảng.
Liên kết giữa books
và publishers
tuy nhiên là khác nhau. Bất kỳ cuốn sách nhất định nào cũng chỉ có thể có một nhà xuất bản (trừ khi trong hệ thống của bạn, các ấn bản khác nhau của một cuốn sách được coi là cùng một cuốn sách). Vì vậy, tất cả những gì bạn cần ở đây là publisher_id
khóa ngoại trong books
Cuối cùng, và quan trọng nhất là bạn đang nhìn vào người đọc / người dùng. Và mối quan hệ của chúng với sách. Đương nhiên, đây cũng là quan hệ N:M. Tôi chắc chắn hy vọng rằng mọi người đọc nhiều hơn một cuốn sách (tất cả chúng ta đều biết điều gì sẽ xảy ra nếu bạn chỉ đọc một cuốn ...) và chắc chắn một cuốn sách được đọc bởi nhiều hơn một người. Điều đó yêu cầu một book_users
bảng kết nối. Câu hỏi thực sự ở đây là, làm thế nào để thiết kế nó. Có ba thiết kế cơ bản.
-
Tách các bảng theo loại quan hệ . (như được nêu bởi @just_somebody) Ưu điểm:Bạn chỉ có CẬP NHẬT và XÓA, không bao giờ CẬP NHẬT. Mặc dù điều này trông khá gọn gàng và phần nào giúp tối ưu hóa truy vấn, nhưng hầu hết thời gian nó không phục vụ mục đích thực tế nào ngoài việc hiển thị một biểu đồ cơ sở dữ liệu lớn.
-
Một bảng có
status
chỉ báo . (như được nêu bởi @Hardcoded) Ưu điểm:Bạn chỉ có một bảng. Nhược điểm:Bạn sẽ có INSERTS, UPDATES và DELETES - một cái gì đó RDBMS có thể dễ dàng xử lý, nhưng có những sai sót vì nhiều lý do (sẽ nói thêm về điều đó sau) Ngoài ra, mộtstatus
trường ngụ ý rằng một người đọc chỉ có thể có một kết nối với sách bất kỳ lúc nào, nghĩa là anh ta chỉ có thể ở trongplan_to_read
,is_reading
hoặchas_read
trạng thái tại bất kỳ thời điểm nào và nó giả định một trật tự trong thời gian điều này xảy ra. Nếu người đó có kế hoạch đọc nó một lần nữa hoặc tạm dừng, sau đó đọc lại từ đầu, v.v., một loạt chỉ báo trạng thái đơn giản như vậy có thể dễ dàng bị lỗi, vì đột nhiên người đóis_reading
bây giờ, mà còn cảhas_read
điều. Đối với hầu hết các ứng dụng, đây vẫn là một cách tiếp cận hợp lý và thường có nhiều cách để thiết kế các trường trạng thái để chúng loại trừ lẫn nhau. -
Nhật ký . Bạn CHÈN mọi trạng thái dưới dạng một hàng mới trong bảng - sự kết hợp giống nhau giữa sách và người đọc sẽ xuất hiện nhiều lần. Bạn CHÈN hàng đầu tiên với
plan_to_read
và một dấu thời gian. Một cái khác cóis_reading
. Sau đó, một cái khác cóhas_read
. Ưu điểm:Bạn sẽ chỉ phải CHÈN các hàng và bạn sẽ có được một trình tự thời gian gọn gàng của những việc đã xảy ra. Nhược điểm:Các phép nối bảng chéo hiện phải xử lý nhiều dữ liệu hơn (và phức tạp hơn) so với các cách tiếp cận đơn giản ở trên.
Bạn có thể tự hỏi mình, tại sao lại nhấn mạnh vào việc bạn CHÈN, CẬP NHẬT hay XÓA trong trường hợp nào? Nói tóm lại, bất cứ khi nào bạn chạy câu lệnh UPDATE hoặc DELETE, bạn rất có thể bị mất dữ liệu. Tại thời điểm đó, bạn cần phải dừng lại trong quá trình thiết kế của mình và nghĩ rằng "Tôi đang đánh mất điều gì ở đây?" Trong trường hợp này, bạn sẽ mất thứ tự thời gian của các sự kiện. Nếu những gì người dùng đang làm với sách của họ là trung tâm của ứng dụng của bạn, thì bạn rất có thể muốn thu thập càng nhiều dữ liệu càng tốt. Ngay cả khi nó không quan trọng ngay bây giờ, đó là loại dữ liệu có thể cho phép bạn làm "phép thuật" sau này. Bạn có thể biết ai đó đang đọc nhanh như thế nào, họ cần bao nhiêu lần để đọc xong một cuốn sách, v.v. Tất cả những điều đó mà không cần yêu cầu người dùng cung cấp thêm bất kỳ thông tin đầu vào nào.
Vì vậy, câu trả lời cuối cùng của tôi thực sự là một câu hỏi:
Chỉnh sửa
Vì có thể không rõ một nhật ký trông như thế nào và nó sẽ hoạt động như thế nào, nên đây là một ví dụ về bảng như vậy:
CREATE TABLE users_reading_log (
user_id INT,
book_id INT,
status ENUM('plans_to_read', 'is_reading', 'has_read'),
ts TIMESTAMP DEFAULT NOW()
)
Bây giờ, thay vì cập nhật bảng "user_read" trong lược đồ được thiết kế của bạn bất cứ khi nào trạng thái của sách thay đổi, giờ đây bạn CHÈN cùng dữ liệu đó trong nhật ký hiện điền với thứ tự thông tin:
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='plans_to_read';
Khi người đó thực sự bắt đầu đọc, bạn thực hiện một đoạn chèn khác:
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='is_reading';
và như thế. Bây giờ bạn có một cơ sở dữ liệu về "sự kiện" và vì cột dấu thời gian tự động điền vào chính nó, nên bây giờ bạn có thể biết điều gì đã xảy ra khi nào. Xin lưu ý rằng hệ thống này không đảm bảo rằng chỉ tồn tại một 'is_reading' cho một cặp sách người dùng cụ thể. Ai đó có thể ngừng đọc và sau đó tiếp tục. Các thành viên tham gia của bạn sẽ phải tính đến điều đó.