Będziesz potrzebować linku N:M między books
i authors
, ponieważ książka może mieć wielu autorów, a każdy autor mógł napisać więcej niż jedną książkę. W RDBMS oznacza to, że będziesz potrzebować written_by
tabela.
Link między books
i publishers
jednak jest inaczej. Każda dana książka może mieć tylko jednego wydawcę (chyba że w Twoim systemie różne wydania książki są uważane za tę samą książkę). Wszystko, czego potrzebujesz, to publisher_id
klucz obcy w books
Na koniec, a co najważniejsze, patrzysz na czytelników/użytkowników. I ich stosunek do książek. Oczywiście jest to również relacja N:M. Mam nadzieję, że ludzie przeczytają więcej niż jedną książkę (wszyscy wiemy, co się stanie, jeśli przeczytasz tylko jedną...) i na pewno książkę czyta więcej niż jedna osoba. To wymaga book_users
tabela połączeń. Prawdziwe pytanie brzmi:jak to zaprojektować. Istnieją trzy podstawowe projekty.
-
Oddziel tabele według typu relacji . (jak opisuje @just_somebody ) Zalety:Masz tylko WSTAWKI i USUWANIA, nigdy AKTUALIZACJA. Chociaż wygląda to całkiem fajnie i nieco pomaga w optymalizacji zapytań, przez większość czasu nie służy to żadnemu celowi poza pokazywaniem dużego wykresu bazy danych.
-
Jedna tabela ze
status
wskaźnik . (zgodnie z opisem @Hardcoded) Zalety:Masz tylko jeden stół. Wady:Będziesz mieć WSTAWKI, AKTUALIZACJE i USUNIĘCIA - coś, co RDBMS może z łatwością obsłużyć, ale ma swoje wady z różnych powodów (więcej o tym później) Również pojedynczystatus
to pole oznacza, że jeden czytelnik może mieć tylko jedno połączenie z książką w danym momencie, co oznacza, że może znajdować się tylko wplan_to_read
,is_reading
lubhas_read
status w dowolnym momencie i przyjmuje kolejność w czasie, gdy to się stanie. Jeśli ta osoba kiedykolwiek zamierzała przeczytać to ponownie , lub wstrzymać, a następnie ponownie przeczytać od początku itp., taka prosta seria wskaźników stanu może łatwo się nie powieść, ponieważ nagle ta osobais_reading
teraz, ale takżehas_read
rzecz. W przypadku większości aplikacji jest to nadal rozsądne podejście i zwykle istnieją sposoby na zaprojektowanie pól stanu tak, aby wzajemnie się wykluczały. -
Dziennik . WSTAWIASZ każdy status jako nowy wiersz w tabeli - ta sama kombinacja książki i czytnika pojawi się więcej niż raz. WSTAWIASZ pierwszy wiersz z
plan_to_read
i sygnaturę czasową. Kolejny zis_reading
. Potem kolejny zhas_read
. Zalety:Będziesz musiał tylko WSTAWIĆ wiersze, a otrzymasz zgrabną chronologię wydarzeń. Wady:Połączenia krzyżowe muszą teraz radzić sobie z dużo większą ilością danych (i być bardziej złożone) niż w prostszych metodach opisanych powyżej.
Możesz zadać sobie pytanie, dlaczego w jakim scenariuszu kładzie się nacisk na WSTAWIANIE, AKTUALIZOWANIE lub USUWANIE? Krótko mówiąc, za każdym razem, gdy uruchamiasz instrukcję UPDATE lub DELETE, jest bardzo prawdopodobne, że w rzeczywistości przegrasz dane. W tym momencie musisz zatrzymać się w procesie projektowania i pomyśleć „Co tu tracę?” W takim przypadku tracisz chronologiczną kolejność wydarzeń. Jeśli to, co użytkownicy robią ze swoimi książkami, jest centrum Twojej aplikacji, możesz bardzo dobrze zebrać jak najwięcej danych. Nawet jeśli w tej chwili nie ma to znaczenia, jest to rodzaj danych, które mogą później pozwolić na „magiczne” działanie. Możesz dowiedzieć się, jak szybko ktoś czyta, ile prób musi dokończyć książki itp. Wszystko to bez pytania użytkownika o dodatkowe informacje.
Tak więc moja ostateczna odpowiedź to właściwie pytanie:
Edytuj
Ponieważ może nie być jasne, jak będzie wyglądał dziennik i jak będzie działał, oto przykład takiej tabeli:
CREATE TABLE users_reading_log (
user_id INT,
book_id INT,
status ENUM('plans_to_read', 'is_reading', 'has_read'),
ts TIMESTAMP DEFAULT NOW()
)
Teraz, zamiast aktualizować tabelę „user_read” w zaprojektowanym schemacie za każdym razem, gdy zmienia się status książki, WSTAWIASZ te same dane do dziennika, który teraz wypełnia chronologię informacji:
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='plans_to_read';
Kiedy ta osoba zacznie czytać, wstawiasz kolejną wstawkę:
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='is_reading';
i tak dalej. Teraz masz bazę danych „zdarzeń”, a ponieważ kolumna sygnatury czasowej automatycznie się zapełnia, możesz teraz stwierdzić, co się stało i kiedy. Należy pamiętać, że ten system nie gwarantuje, że istnieje tylko jeden element „czy_czyta” dla określonej pary książek użytkownika. Ktoś może przestać czytać, a później kontynuować. Twoje połączenia będą musiały to uwzględnić.