Bonjour bestdomdom,
Envoyé par
bestdomdom
Lorsque je crée mes tables, je considère que u_id et q_id sont des clés primaires (et étrangères) de la table Répondre:
Repondre (#u_id, #q_id, degre)
Pouvez-vous me confirmer que c'est suffisant pour garantir que, dans la table Répondre, à chaque couple utilisateur-question (u_id, q_id) ne puisse correspondre qu'un et un seul 'degre' ?
Exemple:
u_id=1 q_id=1 degre=5
u_id=1 q_id=1 degre=7 Impossible car couple (u_id,q_id) existe déjà
u_id=1 q_id=2 degre=7 Possible car couple (u_id,q_id) n'existe pas
Je réponds plus loin.
Au préalable, je voudrais m'assurer que nous sommes sur la même longueur d'onde. Pour des raisons de commodité, je vais reprendre votre sujet au niveau purement logique. Considérons donc les relations entre un utilisateur et une question, traduites sous forme de tables.
La table POSER a la structure suivante (j’ai ôté les "#" qui posent souvent un problème avec la version française des SGBD) :
POSER {U_id, Q_id, Degre}
Dont le prédicat est le suivant :
A l’utilisateur U_id est posée la question Q_id, selon le degré Degre.
Et la table REPONDRE :
REPONDRE {U_id, Q_id, Nb, NbOk}
Dont le prédicat est le suivant :
A la question Q_id, l’utilisateur U_id a répondu Nb fois, dont NbOk fois de façon correcte.
On peut se poser la question suivante : pourquoi des deux tables de pas en faire qu’une ? (Exercice : rédigez-en le prédicat)
QR {U_id, Q_id, Degre, Nb, NbOk}
En effet, du point de vue de la théorie relationnelle, QR représente la jointure naturelle de POSER et de REPONDRE et peut à son tour être décomposée sans perte en POSER et REPONDRE, par application du théorème de Heath (Cf. la fin de ce message).
Mais, il ne faut pas oublier de tenir compte de NULL qui peut nous empoisonner la vie. Voyez par exemple Le bonhomme NULL.
Supposons que tous les attributs de la table QR soient nécessairement valorisés au cours de la même transaction (même COMMIT/ROLLBACK), NULL étant interdit, autrement dit si l’instruction CREATE TABLE est ainsi rédigée :
1 2 3 4 5 6 7 8 9
| CREATE TABLE QR (
U_id Integer NOT NULL
, Q_id Integer NOT NULL
, Degre Integer NOT NULL
, Nb Integer NOT NULL
, NbOk Integer NOT NULL
, Primary Key (U_id, Q_id)
, Foreign Key (U_id) References USER
, Foreign Key (Q_id) References QUESTION) ; |
Alors la table QR est tout à fait acceptable.
Supposons que les attributs de la table QR ne soient pas tous valorisés au cours de la même transaction, cas par exemple des attributs Nb et NbOk qui seraient valorisés postérieurement à l'attribut Degre. La table QR est toujours correcte si vous utilisez des valeurs par défaut :
1 2 3 4 5 6 7 8 9
| CREATE TABLE QR (
U_id Integer NOT NULL
, Q_id Integer NOT NULL
, Degre Integer NOT NULL
, Nb Integer NOT NULL DEFAULT 0
, NbOk Integer NOT NULL DEFAULT 0
, Primary Key (U_id, Q_id)
, Foreign Key (U_id) References USER
, Foreign Key (Q_id) References QUESTION) ; |
Étant convenu que tant qu’un utilisateur n’a pas encore fourni de réponse, Nb et NbOk prendront par défaut la valeur 0.
Maintenant, si vous voulez vous coller des problèmes sur le dos, autorisez le bonhomme NULL à se manifester :
1 2 3 4 5 6 7 8 9
| CREATE TABLE QR (
U_id Integer NOT NULL
, Q_id Integer NOT NULL
, Degre Integer NOT NULL
, Nb Integer NULL
, NbOk Integer NULL
, Primary Key (U_id, Q_id)
, Foreign Key (U_id) References USER
, Foreign Key (Q_id) References QUESTION) ; |
Une autre façon de procéder, mais proprement, consiste à mettre en oeuvre deux tables, à savoir les tables POSER et REPONDRE mentionnées ci-dessus et issues des associations-types Poser et Repondre du MCD :
POSER {U_id, Q_id, Degre}
REPONDRE {U_id, Q_id, Nb, NbOk}
En réponse à votre question initiale, le problème est que l’on peut tout à fait se retrouver avec des réponses à des questions non posées, il suffit que q1 et q2 existent dans la table QUESTION...
1 2 3 4 5
| POSER (U_id, Q_id, , Degre)
u1 q1 d1
REPONDRE (U_id, Q_id, , Nb, NbOk)
u1 q2 1 1 |
Ce qui veut dire que pour éviter cela, il faut établir une contrainte d’inclusion entre les deux tables, ce qui en fait est facile à réaliser :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| CREATE TABLE POSER (
U_id Integer NOT NULL
, Q_id Integer NOT NULL
, Degre Integer NOT NULL
, Primary Key (U_id, Q_id)
, Foreign Key (U_id) References USER
, Foreign Key (Q_id) References QUESTION) ;
CREATE TABLE REPONDRE (
U_id Integer NOT NULL
, Q_id Integer NOT NULL
, Nb Integer NOT NULL
, NbOk Integer NOT NULL
, Primary Key (U_id, Q_id)
, Foreign Key U_id, Q_id) References POSER) ; |
C'est-à-dire en définissant une clé étrangère ad-hoc, entre POSER et REPONDRE, clé étrangère qui impose que chaque couple {U_id, Q_id} dans REPONDRE soit d'abord un couple {U_id, Q_id} dans POSER.
Dans votre exemple, l’alternative consistant à mettre en œuvre, soit la seule table QR, soit le couple POSER, REPONDRE peut paraître futile, mais dans la réalité des grandes bases de données sur lesquelles sont connectés des centaines, voire des milliers d’utilisateurs, on y regarde à deux fois. Par exemple, si on extrapole en remplaçant USER par CLIENT, QUESTION par PRODUIT, POSER par COMMANDE et REPONDRE par FACTURE, on peut se poser le problème de l’encombrement des buffers (et autres caches) par des données inutiles, quand de FACTURE et COMMANDE on fait une seule table CDE_FACT : la partie (non encore connue) FACTURE encombre inutilement la mémoire quand on traite des commandes, on peut de poser le problème de l'inter-blocages des transactions, des droits des utilisateurs sur des tables distinctes ou non, etc.
Mais, Sahib, ceci est une autre histoire.
__________________________________
NB. Théorème de Heath (1971) :
Soit la variable relationnelle R (A, B, C) dans laquelle A, B et C sont des ensembles d’attributs de R.
Si R satisfait à la dépendance fonctionnelle A → B, alors R est égale à la jointure de ses projections sur {A, B} et {A, C}.
(Le terme "variable relationnelle" peut être traduit informellement par le terme "table").
Partager