Bonsoit yrichi,
Un point de détail : votre 1er diagramme n’est pas un MCD, mais un MLD, car dans un MCD, le concept de clé étrangère (FK) n’existe pas : disons que la clé étrangère du MLD est la traduction d’une association à cardinalité maximale 1 du MCD.
La notation que vous utilisez pour ce 1er diagramme est du genre IDEF1X, laquelle se trouve être très rarement utilisée dans ce forum (en plus les associations sont colorisées, cela joue-t-il un rôle formel ?) Aussi serait-il bon en l’occurrence que vous rédigiez en français les règles de gestion des données, par exemple :
(RG1) Un utilisateur a de zéro à plusieurs dossiers ;
(RG2) Un dossier appartient à au moins et au plus un utilisateur ;
Etc.
Cela dit, dans votre 2e diagramme (qui est un MCD), l’association APPARTIENT est une ternaire avec des cardinalités 1,1, ce qui n’est pas conseillé : conservez les deux binaires de votre 1er diagramme.
Association USER - CLIENT :
Ce que j’écris fait référence à la 1re version de votre message. Depuis vous avez changé la règle du jeu, mais je conserve quand même mes commentaires :
En utilisant une loupe, la lunule verte collée à CLIENT donne l’impression qu’un utilisateur est associé de zéro à plusieurs clients. Qu’en est-il ? Quoi qu’il en soit, l’emploi du verbe « être » dans la phrase « donc le client est forcément un User » est erroné : à la limite, un utilisateur peut représenter (merci de choisir le verbe sémantiquement pertinent) plusieurs clients, mais qu’un utilisateur soit plusieurs clients, ça n’a pas de sens. L’emploi du verbe « être » ne serait légitime que si un utilisateur ne représentait qu’un seul client (injection ou bijection).
En passant, de mon côté je renomme USER en UTILISATEUR, car on peut avoir des surprises en passant au niveau SQL : USER fait partie des mots réservés de ce langage (définis par la norme). A noter que si deux utilisateurs ne peuvent pas avoir le même login, alors l’attribut login devra faire l’objet d’un identifiant alternatif (clé candidate alternative au stade SQL), même remarque concernant les dossiers et les fichiers : pour un utilisateur donné, deux dossiers ne devraient pas avoir le même nom et deux fichiers ne devraient pas non plus avoir le même nom. Si en fait pour un utilisateur donné, deux fichiers peuvent avoir le même nom, l’identifiant alternatif sera la paire Cheminfichier, Nomfichier. Ci-dessous, je me suis limité à l’unicité du nom du fichier.
Contrainte de chemin
Selon votre diagramme, rien n’empêche, comme ci-dessous, d’associer les dossiers d’un utilisateur à ceux d’un autre utilisateur dans la table DOSSIER_COMPOSITION(les clés primaires sont soulignées) :
Table UTILISATEUR
IdUser login Mdp ...
------
1 tototo **** ...
2 loulou **** ...
Table DOSSIER
IdDossier IdUser NomDossier
---------
1 1 dossier 47
2 1 dossier 85
3 2 dossier 12
4 2 dossier 85
Table FICHIER
IdFichier IdUser NomFichier
---------
1 1 fichier 01
2 1 fichier 05
3 2 fichier 10
4 2 fichier 01
Table DOSSIER_COMPOSITION
IdDossier IdFichier
--------- ---------
1 1
1 2
2 4
Pour éviter ce genre d’anomalie, il faut mettre en œuvre une contrainte dite de chemin. Pour cela, soit vous développez un trigger, soit vous utilisez l’identification relative, c'est-à-dire en faisant participer l’attribut IdUser à la clé primaire des tables DOSSIER, FICHIER et DOSSIER_COMPOSITION. Les valeurs prises par les attributs IdDossier et IdFichier ne sont plus absolues, mais relatives à l’attribut IdUser.
Exemple de script SQL avec PostgreSQL en ce sens (avec MySQL, remplacer « SERIAL » par « INT NOT NULL AUTO_INCREMENT », et avec SQL Server, remplacer par «INT NOT NULL IDENTITY ») :
Table UTILISATEUR
CREATE TABLE UTILISATEUR
(
IdUser SERIAL
, Login VARCHAR(50) NOT NULL
, Mdp VARCHAR(50) NOT NULL
, CONSTRAINT UTILISATEUR_PK PRIMARY KEY (IdUser)
, CONSTRAINT UTILISATEUR_AK UNIQUE (Login)
) ;
Table DOSSIER
CREATE TABLE DOSSIER
(
IdUser INT NOT NULL
, IdDossier INT NOT NULL
, NomDossier VARCHAR(50) NOT NULL
, CONSTRAINT DOSSIER_PK PRIMARY KEY (IdUser, IdDossier)
, CONSTRAINT DOSSIER_AK UNIQUE (IdUser, NomDossier)
, CONSTRAINT DOSSIER_UTILISATEUR_FK FOREIGN KEY (IdUser)
REFERENCES UTILISATEUR (IdUser)
) ;
Table FICHIER
CREATE TABLE FICHIER
(
IdUser INT NOT NULL
, IdFichier INT NOT NULL
, NomFichier VARCHAR(50) NOT NULL
, CONSTRAINT FICHIER_PK PRIMARY KEY (IdUser, IdFichier)
, CONSTRAINT FICHIER_AK UNIQUE (IdUser, NomFichier)
, CONSTRAINT FICHIER_UTILISATEUR_FK FOREIGN KEY (IdUser)
REFERENCES UTILISATEUR (IdUser)
) ;
TABLE DOSSIER_COMPOSITION
CREATE TABLE DOSSIER_COMPOSITION
(
IdUser INT NOT NULL
, IdDossier INT NOT NULL
, IdFichier INT NOT NULL
, CONSTRAINT DOSSIER_COMPOSITION_PK PRIMARY KEY (IdUser, IdDossier, IdFichier)
, CONSTRAINT DOSSIER_COMPOSITION_DOSSIER_FK FOREIGN KEY (IdUser, IdDossier)
REFERENCES DOSSIER (IdUser, IdDossier)
, CONSTRAINT DOSSIER_COMPOSITION_FICHIER_FK FOREIGN KEY (IdUser, IdFichier)
REFERENCES FICHIER (IdUser, IdFichier)
) ;
Notez dans la table DOSSIER_COMPOSITION l’absence de l’attribut non essentiel Id_Dossier_Composition.
Un micro jeu d’essai :
Table UTILISATEUR
INSERT INTO UTILISATEUR (Login, Mdp) VALUES ('tototo', '*****') ;
INSERT INTO UTILISATEUR (Login, Mdp) VALUES ('loulou', '*****') ;
Table DOSSIER
INSERT INTO DOSSIER (IdUser, IdDossier, NomDossier)
SELECT (SELECT IdUser FROM UTILISATEUR WHERE Login = 'tototo')
, (SELECT COALESCE(MAX(IdDossier) + 1, 1)
FROM DOSSIER AS x JOIN UTILISATEUR AS y on x.IdUser = y.IdUser
WHERE Login = 'tototo')
, 'dossier 47' ;
INSERT INTO DOSSIER (IdUser, IdDossier, NomDossier)
SELECT (SELECT IdUser FROM UTILISATEUR WHERE Login = 'tototo')
, (SELECT COALESCE(MAX(IdDossier) + 1, 1)
FROM DOSSIER AS x JOIN UTILISATEUR AS y on x.IdUser = y.IdUser
WHERE Login = 'tototo')
, 'dossier 85' ;
INSERT INTO DOSSIER (IdUser, IdDossier, NomDossier)
SELECT (SELECT IdUser FROM UTILISATEUR WHERE Login = 'loulou')
, (SELECT COALESCE(MAX(IdDossier) + 1, 1)
FROM DOSSIER AS x JOIN UTILISATEUR AS y on x.IdUser = y.IdUser
WHERE Login = 'loulou')
, 'dossier 12' ;
INSERT INTO DOSSIER (IdUser, IdDossier, NomDossier)
SELECT (SELECT IdUser FROM UTILISATEUR WHERE Login = 'loulou')
, (SELECT COALESCE(MAX(IdDossier) + 1, 1)
FROM DOSSIER AS x JOIN UTILISATEUR AS y on x.IdUser = y.IdUser
WHERE Login = 'loulou')
, 'dossier 85' ;
Au résultat :
IdUser IdDossier NomDossier
------ ---------
1 1 dossier 47
1 2 dossier 85
2 1 dossier 12
2 2 dossier 85
Table FICHIER
INSERT INTO FICHIER (IdUser, IdFichier, NomFichier)
SELECT (SELECT IdUser FROM UTILISATEUR WHERE Login = 'tototo')
, (SELECT COALESCE(MAX(IdFichier) + 1, 1)
FROM FICHIER AS x JOIN UTILISATEUR AS y ON x.IdUser = y.IdUser
WHERE Login = 'tototo')
, 'fichier 01' ;
INSERT INTO FICHIER (IdUser, IdFichier, NomFichier)
SELECT (SELECT IdUser FROM UTILISATEUR WHERE Login = 'tototo')
, (SELECT COALESCE(MAX(IdFichier) + 1, 1)
FROM FICHIER AS x JOIN UTILISATEUR AS y on x.IdUser = y.IdUser
WHERE Login = 'tototo')
, 'fichier 05' ;
INSERT INTO FICHIER (IdUser, IdFichier, NomFichier)
SELECT (SELECT IdUser FROM UTILISATEUR WHERE Login = 'loulou')
, (SELECT COALESCE(MAX(IdFichier) + 1, 1)
FROM FICHIER AS x JOIN UTILISATEUR AS y on x.IdUser = y.IdUser
WHERE Login = 'loulou')
, 'fichier 10' ;
INSERT INTO FICHIER (IdUser, IdFichier, NomFichier)
SELECT (SELECT IdUser FROM UTILISATEUR WHERE Login = 'loulou')
, (SELECT COALESCE(MAX(IdFichier) + 1, 1)
FROM FICHIER AS x JOIN UTILISATEUR AS y on x.IdUser = y.IdUser
WHERE Login = 'loulou')
, 'fichier 01' ;
Au résultat :
IdUser IdFichier NomFichier
------ ---------
1 1 fichier 01
1 2 fichier 05
2 1 fichier 10
2 2 fichier 01
Table DOSSIER_COMPOSITION
INSERT INTO DOSSIER_COMPOSITION (IdUser, IdDossier, IdFichier)
SELECT (SELECT IdUser
FROM UTILISATEUR
WHERE Login = 'tototo')
, (SELECT IdDossier
FROM DOSSIER AS x JOIN UTILISATEUR AS y ON x.IdUser = y.IdUser
WHERE Login = 'tototo' AND NomDossier = 'dossier 47')
, (SELECT IdFichier
FROM FICHIER AS x JOIN UTILISATEUR AS y ON x. IdUser = y.IdUser
WHERE Login = 'tototo' AND NomFichier = 'fichier 01') ;
INSERT INTO DOSSIER_COMPOSITION (IdUser, IdDossier, IdFichier)
SELECT (SELECT IdUser
FROM UTILISATEUR
WHERE Login = 'tototo')
, (SELECT IdDossier
FROM DOSSIER AS x JOIN UTILISATEUR AS y ON x. IdUser = y.IdUser
WHERE Login = 'tototo' AND NomDossier = 'dossier 47')
, (SELECT IdFichier
FROM FICHIER AS x JOIN UTILISATEUR AS y ON x. IdUser = y.IdUser
WHERE Login = 'tototo' AND NomFichier = 'fichier 05') ;
INSERT INTO DOSSIER_COMPOSITION (IdUser, IdDossier, IdFichier)
SELECT (SELECT IdUser
FROM UTILISATEUR
WHERE Login = 'tototo')
, (SELECT IdDossier
FROM DOSSIER AS x JOIN UTILISATEUR AS y ON x. IdUser = y.IdUser
WHERE Login = 'tototo' AND NomDossier = 'dossier 85')
, (SELECT IdFichier
FROM FICHIER AS x JOIN UTILISATEUR AS y ON x.IdUser = y.IdUser
WHERE Login = 'tototo' AND NomFichier = 'fichier 05') ;
INSERT INTO DOSSIER_COMPOSITION (IdUser, IdDossier, IdFichier)
SELECT (SELECT IdUser
FROM UTILISATEUR
WHERE Login = 'loulou')
, (SELECT IdDossier
FROM DOSSIER AS x JOIN UTILISATEUR AS y ON x. IdUser = y.IdUser
WHERE Login = 'loulou' AND NomDossier = 'dossier 85')
, (SELECT IdFichier
FROM FICHIER AS x JOIN UTILISATEUR AS y ON x.IdUser = y.IdUser
WHERE Login = 'loulou' AND NomFichier = 'fichier 01') ;
Au résultat :
IdUser IdDossier IdFichier
------ --------- ---------
1 1 1
1 1 2
1 2 2
2 2 2
Les INSERT que j’ai codés peuvent paraître compliqués, mais j’ai préféré accéder aux données à la façon de l’utilisateur, c'est-à-dire au moyen des clés « naturelles » : Login, nom du dossier, nom du fichier. On s’y fait assez rapidement...
Envoyé par
yrichi
je ne sais pas comment représenter le fait qu'un dossier est contenu dans un autre dossier.
Votre 2e diagramme est correct. Votre association donne lieu au script SQL suivant :
CREATE TABLE DOSSIER_HIERARCHIE
(
IdUser INT NOT NULL
, IdDossier INT NOT NULL
, IdDossierParent INT NOT NULL
, CONSTRAINT DOSSIER_HIERARCHIE_PK PRIMARY KEY (IdUser, IdDossier)
, CONSTRAINT DOSSIER_HIERARCHIE_DOSSIER_FK FOREIGN KEY (IdUser, IdDossier)
REFERENCES DOSSIER (IdUser, IdDossier)
, CONSTRAINT DOSSIER_HIERARCHIE_DOSSIER_PARENT_FK FOREIGN KEY (IdUser, IdDossierParent)
REFERENCES DOSSIER (IdUser, IdDossier)
) ;
A propos de votre 1er diagramme, vous écrivez : « le champ nivhierachique me facilitera l'affichage ». Vous injectez une redondance, ce qui est malsain. Vous pouvez le faire, mais à condition lors de chaque INSERT, UPDATE, DELETE touchant à la table DOSSIER, de contrôler par un trigger que la valeur prise par cet attribut (et pas champ, concept ayant trait aux fichiers) est valide par rapport à la réalité.
Cette remarque vaut aussi pour l’attribut NomDossierAncetre (table FICHIER).
Quel est votre SGBD ?
Partager