Ще ви трябва N:M връзка между books
и authors
, тъй като една книга може да има няколко автора и всеки автор може да е написал повече от една книга. В RDBMS това означава, че ще ви трябва written_by
таблица.
Връзката между books
и publishers
обаче е различно. Всяка дадена книга може да има само един издател (освен ако във вашата система различни издания на книга се считат за една и съща книга). Така че всичко, от което се нуждаете тук, е publisher_id
външен ключ в books
И накрая, и най-важното, вие гледате към читателите/потребителите. И връзката им с книгите. Естествено, това също е връзка N:M. Сигурно се надявам, че хората четат повече от една книга (всички знаем какво се случва, ако някога прочетете само една...) и със сигурност една книга се чете от повече от един човек. Това изисква book_users
свързваща маса. Истинският въпрос тук е как да го проектираме. Има три основни дизайна.
-
Разделете таблиците по тип връзка . (както е посочено от @just_somebody ) Предимства:Имате само INSERTS и DELETES, никога UPDATES. Въпреки че това изглежда някак спретнато и донякъде помага при оптимизирането на заявките, през повечето време няма действителна цел, освен показване на голяма диаграма на базата данни.
-
Една таблица със
status
индикатора . (както е посочено от @Hardcoded) Предимства:Имате само една маса. Недостатъци:Ще имате INSERTS, UPDATES и DELETES - нещо, което RDBMS може лесно да се справи, но което има своите недостатъци по различни причини (повече за това по-късно) Също така, единstatus
полето означава, че един читател може да има само една връзка с книгата по всяко време, което означава, че може да бъде само вplan_to_read
,is_reading
илиhas_read
състояние във всеки момент от време и предполага ред във времето, когато това се случва. Ако този човек някога би планирал да го прочете отново , или пауза, след което отново прочетете отначало и т.н., такава проста серия от индикатори за състояние може лесно да се провали, защото изведнъж този човекis_reading
сега, но също иhas_read
нещото. За повечето приложения това все още е разумен подход и обикновено има начини за проектиране на полета за състояние, така че да се изключват взаимно. -
Дневник . ВМЕСВАТЕ всеки статус като нов ред в таблица - една и съща комбинация от книга и четец ще се появи повече от веднъж. ВМЕСВАТЕ първия ред с
plan_to_read
, и времева марка. Друг сis_reading
. След това още един сhas_read
. Предимства:Винаги ще трябва да ВМЕСВАТЕ редове и ще получите чиста хронология на нещата, които са се случили. Недостатъци:Съединенията на кръстосани таблици вече трябва да се справят с много повече данни (и да са по-сложни), отколкото в по-простите подходи по-горе.
Може да се запитате защо се набляга на това дали ВМЕСВАТЕ, АКТУАЛИЗИРАТЕ или ИЗТРИВАТЕ в какъв сценарий? Накратко, когато стартирате оператор UPDATE или DELETE, е много вероятно да загубите данни. В този момент трябва да спрете в процеса на проектиране и да помислите „Какво губя тук?“ В този случай губите хронологичния ред на събитията. Ако това, което потребителите правят със своите книги, е в центъра на вашето приложение, може да искате да съберете колкото се може повече данни. Дори ако в момента няма значение, това е типът данни, който може да ви позволи да правите „магия“ по-късно. Можете да разберете колко бързо някой чете, колко опита му трябва, за да завърши книга и т.н. Всичко това, без да питате потребителя за допълнителен вход.
И така, окончателният ми отговор всъщност е въпрос:
Редактиране
Тъй като може да не е ясно как би изглеждал дневникът и как ще функционира, ето пример за такава таблица:
CREATE TABLE users_reading_log (
user_id INT,
book_id INT,
status ENUM('plans_to_read', 'is_reading', 'has_read'),
ts TIMESTAMP DEFAULT NOW()
)
Сега, вместо да актуализирате таблицата "user_read" във вашата проектирана схема всеки път, когато състоянието на книга се промени, сега ВМЕСВАТЕ същите тези данни в дневника, който сега се изпълва с хронология на информацията:
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='plans_to_read';
Когато този човек действително започне да чете, правите друго вмъкване:
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='is_reading';
и така нататък. Сега имате база данни със „събития“ и тъй като колоната с времеви печат се запълва автоматично, вече можете да кажете какво се е случило кога. Моля, имайте предвид, че тази система не гарантира, че съществува само едно 'is_reading' за конкретна двойка потребител-книга. Някой може да спре да чете и по-късно да продължи. Вашите присъединявания ще трябва да отчитат това.