Vous aurez besoin d'un lien N:M entre books
et authors
, car un livre peut avoir plusieurs auteurs et chaque auteur peut avoir écrit plusieurs livres. Dans un SGBDR, cela signifie que vous aurez besoin d'un written_by
tableau.
Le lien entre les books
et publishers
cependant est différent. Un livre donné ne peut avoir qu'un seul éditeur (sauf si dans votre système différentes éditions d'un livre sont considérées comme le même livre). Donc tout ce dont vous avez besoin ici est un publisher_id
clé étrangère dans books
Enfin, et surtout, vous regardez les lecteurs / utilisateurs. Et leur rapport aux livres. Naturellement, c'est aussi une relation N:M. J'espère bien que les gens lisent plus d'un livre (nous savons tous ce qui se passe si vous n'en lisez qu'un...) et sûrement qu'un livre est lu par plus d'une personne. Cela nécessite un book_users
tableau de connexion. La vraie question ici est de savoir comment le concevoir. Il existe trois conceptions de base.
-
Séparer les tables par type de relation . (comme indiqué par @just_somebody ) Avantages :Vous n'avez que des INSERTS et des SUPPRESSIONS, jamais des MISES À JOUR. Bien que cela semble plutôt soigné et aide quelque peu à optimiser les requêtes, la plupart du temps, cela ne sert à rien d'autre que de montrer un grand graphique de base de données.
-
Une table avec un
status
indicateur . (comme indiqué par @Hardcoded) Avantages :Vous n'avez qu'une seule table. Inconvénients :vous aurez des INSERTS, des UPDATES et des DELETE - quelque chose que RDBMS peut facilement gérer, mais qui a ses défauts pour diverses raisons (plus à ce sujet plus tard) En outre, un seulstatus
implique qu'un lecteur ne peut avoir qu'une seule connexion au livre à tout moment, ce qui signifie qu'il ne peut être que dans leplan_to_read
,is_reading
ouhas_read
statut à tout moment, et il suppose un ordre dans le temps où cela se produit. Si jamais cette personne envisageait de le relire encore , ou faire une pause, puis relire depuis le début, etc., une série aussi simple d'indicateurs d'état peut facilement échouer, car tout d'un coup cette personneis_reading
maintenant, mais aussihas_read
la chose. Pour la plupart des applications, il s'agit toujours d'une approche raisonnable, et il existe généralement des moyens de concevoir des champs d'état afin qu'ils s'excluent mutuellement. -
Un journal . Vous INSÉREZ chaque statut en tant que nouvelle ligne dans un tableau - la même combinaison de livre et de lecteur apparaîtra plus d'une fois. Vous INSÉREZ la première ligne avec
plan_to_read
, et un horodatage. Un autre avecis_reading
. Puis un autre avechas_read
. Avantages :vous n'aurez qu'à INSÉRER des lignes et vous obtiendrez une chronologie claire des événements qui se sont produits. Inconvénients :les jointures entre tables doivent désormais traiter beaucoup plus de données (et être plus complexes) que dans les approches plus simples ci-dessus.
Vous pouvez vous demander pourquoi l'accent est-il mis sur l'insertion, la mise à jour ou la suppression dans quel scénario ? En bref, chaque fois que vous exécutez une instruction UPDATE ou DELETE, vous êtes très susceptible de perdre Les données. À ce stade, vous devez vous arrêter dans votre processus de conception et penser "Qu'est-ce que je perds ici ?" Dans ce cas, vous perdez l'ordre chronologique des événements. Si ce que les utilisateurs font avec leurs livres est au centre de votre application, vous voudrez peut-être collecter autant de données que possible. Même si cela n'a pas d'importance pour le moment, c'est le type de données qui pourrait vous permettre de faire de la "magie" plus tard. Vous pouvez savoir à quelle vitesse quelqu'un lit, combien de tentatives il a besoin pour terminer un livre, etc. Tout cela sans demander à l'utilisateur une entrée supplémentaire.
Donc, ma réponse finale est en fait une question :
Modifier
Étant donné qu'il n'est peut-être pas clair à quoi ressemblerait un journal et comment il fonctionnerait, voici un exemple d'un tel tableau :
CREATE TABLE users_reading_log (
user_id INT,
book_id INT,
status ENUM('plans_to_read', 'is_reading', 'has_read'),
ts TIMESTAMP DEFAULT NOW()
)
Maintenant, au lieu de mettre à jour la table "user_read" dans votre schéma conçu chaque fois que le statut d'un livre change, vous INSÉREZ maintenant ces mêmes données dans le journal qui se remplit désormais d'une chronologie d'informations :
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='plans_to_read';
Lorsque cette personne commence réellement à lire, vous faites une autre insertion :
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='is_reading';
etc. Vous avez maintenant une base de données "d'événements" et puisque la colonne d'horodatage se remplit automatiquement, vous pouvez maintenant dire ce qui s'est passé quand. Veuillez noter que ce système ne garantit pas qu'un seul 'is_reading' existe pour une paire utilisateur-livre spécifique. Quelqu'un pourrait arrêter de lire et continuer plus tard. Vos jointures devront en tenir compte.