Ave !
Paprick, j’ai regardé ce qu’on pouvait faire avec la contrainte d’inclusion selon laquelle Raoul ne peut conduire un char Patton que s’il a le permis correspondant.
Partons du MCD :

Et voyons voir la traduction en SQL, sans tenir compte de la contrainte :
CREATE TABLE PERSONNE
(
PersonneId INT NOT NULL
, PersonneNom VARCHAR(32) NOT NULL
, PRIMARY KEY (PersonneId)
);
CREATE TABLE CATEGORIE
(
CategorieId INT NOT NULL
, CategorieNom VARCHAR(32) NOT NULL
, PRIMARY KEY (CategorieId)
, UNIQUE (CategorieNom)
);
CREATE TABLE PSN_CAT
(
PersonneId INT NOT NULL
, CategorieId INT NOT NULL
, PermisDate DATE NOT NULL
, PRIMARY KEY (PersonneId, CategorieId)
, CONSTRAINT PSN_CAT_PSN_FK
FOREIGN KEY (PersonneId)
REFERENCES PERSONNE (PersonneId)
ON DELETE CASCADE
, CONSTRAINT PSN_CAT_CAT_FK
FOREIGN KEY (CategorieId)
REFERENCES CATEGORIE (CategorieId)
ON DELETE NO ACTION
);
CREATE TABLE VEHICULE
(
VehiculeId INT NOT NULL,
CategorieId INT NOT NULL,
VehiculeImmat VARCHAR(12) NOT NULL,
PRIMARY KEY (VehiculeId),
UNIQUE (VehiculeImmat),
UNIQUE (VehiculeId, CategorieId), -- pour inclusion
CONSTRAINT VEH_CAT_FK
FOREIGN KEY (CategorieId)
REFERENCES CATEGORIE (CategorieId)
ON DELETE NO ACTION
);
CREATE TABLE CONDUIRE
(
PersonneId INT NOT NULL
, VehiculeId INT NOT NULL
, PRIMARY KEY (PersonneId, VehiculeId)
, CONSTRAINT CON_PSN_FK
FOREIGN KEY (PersonneId)
REFERENCES PERSONNE (PersonneId)
ON DELETE CASCADE
, CONSTRAINT CON_VEH_FK
FOREIGN KEY (VehiculeId)
REFERENCES VEHICULE (VehiculeId)
ON DELETE CASCADE
);
Créons quelques lignes dans les tables :
INSERT INTO PERSONNE VALUES (1, 'Fernand') ;
INSERT INTO PERSONNE VALUES (2, 'Raoul') ;
INSERT INTO PERSONNE VALUES (3, 'Paul') ;
SELECT * FROM PERSONNE ;
PersonneId PersonneNom
1 Fernand
2 Raoul
3 Paul
INSERT INTO CATEGORIE VALUES (1, 'catégorie VL') ;
INSERT INTO CATEGORIE VALUES (2, 'catégorie PL') ;
INSERT INTO CATEGORIE VALUES (3, 'char Patton') ;
SELECT * FROM CATEGORIE ;
CategorieId CategorieNom
1 catégorie VL
2 catégorie PL
3 char Patton
INSERT INTO PSN_CAT VALUES (1, 1, '1950-01-04') ;
INSERT INTO PSN_CAT VALUES (1, 2, '1960-01-01') ;
INSERT INTO PSN_CAT VALUES (1, 3, '1952-12-21') ;
INSERT INTO PSN_CAT VALUES (2, 1, '1950-01-05') ;
INSERT INTO PSN_CAT VALUES (2, 2, '1950-01-05') ;
SELECT * FROM PSN_CAT ;
PersonneId CategorieId PermisDate
1 1 1950-01-04
1 2 1960-01-01
1 3 1952-12-21
2 1 1950-01-05
2 2 1950-01-05
INSERT INTO VEHICULE VALUES (1, 1, '1A75') ;
INSERT INTO VEHICULE VALUES (2, 1, '1Z44') ;
INSERT INTO VEHICULE VALUES (3, 2, '1H91') ;
INSERT INTO VEHICULE VALUES (4, 3, '9G38') ;
SELECT * FROM VEHICULE ;
VehiculeId CategorieId VehiculeImmat
1 1 1A75
2 1 1Z44
3 2 1H91
4 3 9G38
INSERT INTO CONDUIRE VALUES (1, 1) ;
INSERT INTO CONDUIRE VALUES (1, 2) ;
INSERT INTO CONDUIRE VALUES (1, 4) ;
INSERT INTO CONDUIRE VALUES (2, 3) ;
SELECT * FROM CONDUIRE ;
PersonneId VehiculeId
1 1
1 2
1 4
2 3
A ce stade, rien n’interdit que Raoul conduise un char Patton, alors qu’il n’a pas le permis ad-hoc. Pour empêcher cela, on crée l’assertion qui va bien, quelque chose comme cela :
CREATE ASSERTION INCL_ASSERT
CHECK
(NOT EXISTS
(
SELECT *
FROM
(
SELECT e.personneid, d.categorieid
FROM VEHICULE AS d INNER JOIN CONDUIRE AS e ON d.vehiculeid = e.vehiculeid
) AS s
WHERE NOT EXISTS
(
SELECT *
FROM
(
SELECT e.personneid, d.categorieid
FROM VEHICULE as d INNER JOIN CONDUIRE AS e ON d.vehiculeid = e.vehiculeid
INNER JOIN PSN_CAT c ON c.categorieid = d.categorieid
AND e.personneid = c.personneid
) AS a
WHERE s.personneid = a.personneid
AND s.categorieid = a.categorieid
)
);
Peu importe la lourdeur du code de cette assertion, je fais simplement observer que les 3 tables impliquées sont celles qui correspondent aux 3 associations du MCD.
Maintenant, je ne sache pas que les éditeurs de SGBD aient envie de proposer l’instruction CREATE ASSERTION, laquelle fait quand même partie de la norme SQL depuis une trentaine d’années (SQL/92 me semble-t-il). Cette instruction est déclarée gourmande par ses détracteurs, qui attendent sans doute l’utilisation des ordinateurs quantiques pour changer d’avis....
Alors, que faire ?
Intégrer la contrainte CHECK dans la table CONDUIRE ? Niet ! Les SGBD déjouent la manoeuvre...
Par exemple, avec SQL Server :

Envoyé par
SQL Server
Msg 1046, Niveau 15, État 1
Les sous-requêtes ne sont pas autorisées dans ce contexte. Seules sont permises les expressions scalaires.
C’est-à-dire que seuls les contrôles les plus basiques sont légaux.
En passer par des triggers ? On se met à programmer avec les BEGIN/AFTER, for each row, etc.
Avant d’en arriver là, on peut préférer mettre en oeuvre une table reprenant la contrainte d’inclusion :
CREATE TABLE INCLUSION
(
PersonneId INT NOT NULL
, VehiculeId INT NOT NULL
, CategorieId INT NOT NULL
, PRIMARY KEY (PersonneId, VehiculeId)
, CONSTRAINT PSN_CAT_FK
FOREIGN KEY (PersonneId, CategorieId)
REFERENCES PSN_CAT (PersonneId, CategorieId)
ON DELETE NO ACTION
, CONSTRAINT VEH_FK
FOREIGN KEY (VehiculeId, CategorieId)
REFERENCES VEHICULE (VehiculeId, CategorieId)
ON DELETE CASCADE
);
Avec l'ajout d’une clé étrangère dans la table CONDUIRE (du coup la clé étrangère CON_VEH_FK dans la table CONDUIRE peut être supprimée) :
ALTER TABLE CONDUIRE
ADD CONSTRAINT INCLUSION_FK
FOREIGN KEY (PersonneId, VehiculeId)
REFERENCES INCLUSION (PersonneId, VehiculeId)
;
Quelques inserts de contrôle dans la table INCLUSION :
INSERT INTO INCLUSION VALUES (1, 1, 1) ; -- psn, veh, cat
INSERT INTO INCLUSION VALUES (1, 2, 1) ; -- psn, veh, cat
INSERT INTO INCLUSION VALUES (1, 4, 3) ; -- psn, veh, cat
INSERT INTO INCLUSION VALUES (2, 3, 2) ; -- psn, veh, cat
Et tentative d'infraction dans la table CONDUIRE (Raoul et son char Patton) :
INSERT INTO CONDUIRE VALUES (2, 4)
=>

Envoyé par
SQL Server
Msg 547, Niveau 16, État 0
L'instruction INSERT est en conflit avec la contrainte FOREIGN KEY "INCLUSION_FK".
Mission accomplie.
Bien entendu la table INCLUSION ne respecte pas la deuxième forme normale puisque l’attribut CategorieId dépend de l’attribut VehiculeId, donc d’une partie de la clé primaire, d’où redondance, mais on a quand même mis ceinture, bretelles et épingle à nourrice. 
Vos suggestions ?
Partager