Bonsoir,
Pauvre Michel, ça vous tombe dessus comme à Gravelotte...
Pour ma part, je rappelle qu’il s’agit de faire soigneusement la distinction entre le Modèle Relationnel de Données (le Relationland où réside la théorie relationnelle) et son avatar SQL (Sorry Query Language), le Askew Wall qui l’entoure.
Ainsi l’outer join n’existe pas en relationnel, mais bien dans SQL, car il est une conséquence de NULL. Je ne dis pas qu’il faille systématiquement bannir cet opérateur et ce marqueur, mais il y a bien des précautions à prendre dans leur utilisation, c’est un peu comme la dynamite, si on ne sait pas maîtriser ça complètement, ça peut nous péter dans la face...
Des bouquets et des fleurs.
Quand je sqlise, il m’arrive d’utiliser NULL, par exemple dans ce post, quand il s’agit de mettre en commun les commentaires des utilisateurs (les clients) concernant fleurs et bouquets d’icelles :
Inévitablement le cardinalités 0,1 du MCD entraînent la mise en oeuvre de NULL dans la table SQL Commentaire (colonnes bouquetId et fleurId) :
1 2 3 4 5 6 7 8 9 10 11 12 13
| CREATE TABLE Commentaire
( commentaireId INT IDENTITY
, utilisateurId INT NOT NULL
, commentaireTitre VARCHAR(50) NOT NULL
, commmentaireContenu VARCHAR(max) NOT NULL
, commentaireDate DATE NOT NULL
, bouquetId INT, fleurId INT
, CONSTRAINT Commentaire_PK PRIMARY KEY(commentaireId)
, CONSTRAINT Commentaire_Bouquet_FK FOREIGN KEY(bouquetId) REFERENCES Bouquet(bouquetId)
, CONSTRAINT Commentaire_Fleur_FK FOREIGN KEY(fleurId) REFERENCES Fleur(fleurId)
, CONSTRAINT Commentaire_Utilisateur_FK FOREIGN KEY(utilisateurId) REFERENCES Utilisateur(utilisateurId)
, CONSTRAINT Commentaire_soit_bouquet_soit_fleur CHECK
(bouquetId is not NULL AND fleurId is NULL OR bouquetId is NULL AND fleurId is not NULL)) ; |
Notez la contrainte Commentaire_soit_bouquet_soit_fleur
Prise en compte de quelques commentaires de clients contents :
1 2 3
| insert into Commentaire values
(2, 'Un beau bouquet !', 'Ma femme sera contente !', '10/05/2022', 1, NULL)
, (2, 'Un beau bouquet !', 'Ma femme va être ravie !', '10/05/2022', NULL, 2) |
Les précautions étant prises, on peut consulter la table Commentaire (avec référence à la table Utilisateur, pour connaître les auteurs des commentaires) :
1 2 3 4 5 6 7 8
| select utilisateurNom as Nom
, commentaireTitre as Titre, commmentaireContenu as Contenu
, coalesce(b.bouquetNom, '/') as Bouquet
, coalesce(f.fleurNom,'/') as Fleur
from Commentaire as c
join Utilisateur as u on c.utilisateurId = u.utilisateurId
left join Bouquet as b on c.bouquetId = b.bouquetId
left join Fleur as f on c.fleurId = f.fleurId ; |
=>
Nom Titre Contenu Bouquet Fleur
Naudin Un beau bouquet ! Ma femme sera contente ! bouquet de saison /
Naudin Un beau bouquet ! Ma femme va être ravie ! / pivoine
Notez l’emploi de Coalesce pour afficher par exemple "/" plutôt que NULL.
Théorème de Heath
Prenons le cas du théorème de Heath, omniprésent dans la normalisation des bases de données :
Si la relation R {A, B, C} satisfait à la dépendance fonctionnelle A → B, alors R peut être décomposée selon ses projections R1 {A, B} et R2 {A, C}, avec préservation du contenu de la base de données, c'est-à-dire que l’on retrouve très exactement R par la jointure naturelle de R1 et de R2.
Avec SQL, ce théorème est massacré si A peut être marqué null. Exemple :
1 2 3 4 5 6 7 8 9 10 11 12
| create table R
(
a varchar(8)
, b varchar(8)
, c varchar(8)
)
;
insert into R values
('a1', 'b1', 'c1')
, (null, 'b2', 'c2')
;
select 'R' as 'Table', * from R ; |
=>
Table a b c
R a1 b1 c1
R NULL b2 c2
1 2 3 4 5 6 7 8 9 10
| create table R1
(
a varchar(8)
, b varchar(8)
)
;
insert into R1 (a, b)
select a, b from R
;
select 'R1' as 'Table', * from R1 ; |
=>
Table a b
R1 a1 b1
R1 NULL b2
1 2 3 4 5 6 7 8 9 10
| create table R2
(
a varchar(8)
, c varchar(8)
)
;
insert into R2 (a, c)
select a, c from R
;
select 'R2' as 'Table', * from R2 ; |
=>
Table a c
R2 a1 c1
R2 NULL c2
1 2 3
| select 'natural' as 'Table', R1.a, b, c
from R1 join R2 on R1.a = R2.a
; |
=>
Table a b c
natural a1 b1 c1
On n’a pas retrouvé R
Plongeons dans le glauque avec Outer :
1 2
| select 'left' as 'Table', R1.a, b, c
from R1 left outer join R2 on R1.a = R2.a |
=>
Table a b c
left a1 b1 c1
left NULL b2 NULL
1 2
| select 'right' as 'Table', R1.a, b, c
from R1 right outer join R2 on R1.a = R2.a ; |
=>
Table a b c
right a1 b1 c1
right NULL NULL c2
1 2
| select 'full' as 'Table', R1.a, b, c
from R1 full outer join R2 on R1.a = R2.a ; |
=>
Table a b c
full a1 b1 c1
full NULL NULL c2
full NULL b2 NULL
Si vous vous y retrouvez instantanément, vous avez de la chance, ça n’est pas mon cas...
Envoyé par
Michel.Priori
une colonne contient
exactement une valeur du domaine de référence
en écrivant "au plus une valeur" le principe de colonne (vecteur) peut être remis en cause. balot.
Votre citation est incomplète et vous en tirez des conséquences qui n’ont rien à voir. Pour ma part, j’ai écrit :
Envoyé par
fsmrel
Chaque intersection d’une ligne et d’une colonne contient exactement une valeur du domaine de référence de celle-ci (et rien d’autre).
C’est bien à l’intersection d’une ligne et d’une colonne qu’on trouve une valeur.
Si l’on admettait la légitimité de Null (cas de SQL), il faudrait écrire quelque chose du genre :
Chaque intersection d’une ligne et d’une colonne, soit contient une valeur, soit est marquée NULL.
Partager