coincoins

SQL.LID - Les jointures



Les Jointures, ou l'art d'associer les tables

Types d'associations
Associations hiérarchiques ou non, on remet une couche sur le schéma relationnel pour bien comprendre les jointures et leur importance.

1. Association hiérarchique parent-enfant

Comment obtenir la réponse à la question :

  • quelle est la liste des numéros de commandes que le client SAI a passé ?

Cette liste correspond à la liste des commandes dont le numéro de client est égal à celui du client qui porte le nom SAI.

 

Soit les commandes 1 et 3

Association hiérarchique
Client
noClinomCliadrCli
1Air FranceC'est par ici
15SAISur la Terre
357TimkenQuelque part
table "parent"
Commande
cdeNumcdeDatenoCli
101/01/0415
201/01/04357
301/01/0515
table "enfant"

la table enfant porte la clé étrangère vers la table parent

Cette association est dite hiérarchique car :

  • pour une ligne de la table enfant (commande) on trouve une et une seule ligne de la table parent (Client).
  • et pour une ligne de la table parent (Client) on trouve une à plusieurs lignes de la table enfant (commande).

2. Association non hiérarchique

Comment obtenir la réponse à la question :

  • Quelle sont les prix des articles de la commande du 02/01/04 ?

Pour cela, il faut observer le contenu de

  • Commande pour obtenir le numéro de la commande (2), puis
  • Contenir, qui associe la commande aux articles qu'elle contient (la cde 2 contient les articles de ref. A1 et A2) et
  • Article pour obtenir les designations des articles trouvés (A1=>ancre et A2=>manille).

La réponse est donc ancre et manille

Pour y parvenir, nous avons parcourus les liens entre les tables

 

Cette association est dite non hiérarchique car :

  • pour 1 ligne de la table à gauche (commande) on trouve une à plusieurs lignes de la table à droite (Article).
  • et pour 1 ligne de la table à droite (Article) on trouve une à plusieurs lignes de la table à gauche (Commande).

La table du centre représente l'association entre celle de droite et celle de gauche.

Association non hiérarchique
Commande
cdeNumcdeDatenoCli
101/01/0415
202/01/04357
303/01/0415
Contenir
noCderefArtqté
1A15
1A31
2A14
2A230
3A225
Article
refArtdescArtprix
A1ancre49,00
A2manille2,50
A3sextant1250,00

Attention

la clé primaire de Contenir est le couple (noCde, refArt).

Par définition, celle-ci ne peut pas avoir deux valeurs identiques (donc aucun couple identique).

Donc une commande (noCde) ne peut contenir plusieurs ligne du même article.
C'est pourquoi on a ajouté la quantité dans la table contenir.

En effectuant des requêtes qui font la liaison, la jointure entre plusieurs tables, on effectue une ... jointure SQL

Top of page

La jointure SQL
Le coeur du problème : comment faire.

On réalise simplement la jointure en SQL en disant que la clé primaire d'une table référencée est égale à la clé étrangère de l'autre table.

1. Premier exemple : quelle est la liste des numéros de commandes que le client SAI a passé ?

On reprend les tables précédentes et on écrit :

 
SELECT cdeNum
FROM Commande, Client
WHERE Commande.noCli = Client.noCli -- <= jointure entre le n° de client de Commande et de Client
AND nomCli="SAI";

Explications :

  1. nomCli = "SAI" nous permet de savoir que le client "SAI" a pour numéro la valeur 15. Bon.
  2. Avec ce numéro, on va chercher toutes les ligne de commande qui ont ce numéro : Commande.noCli = Client.noCli
  3. Enfin, on récupère uniquement le numéro des commandes dans : SELECT cdeNum

On obtient bien 1 et 3. CQFD.

2. Second exemple : quelle est la description des articles contenus dans la commande du 02/01/04 ?

Idem et on écrit :

 
SELECT descArt
FROM Commande, Contenir, Article
WHERE Commande.noCde=Contenir.noCde -- = jointure 1, entre Commande et Contenir
AND Contenir.refArt=Article.refArt  -- = jointure 2, entre Contenir et Article
AND dateCde="2004/01/02";

Explications :

  • dateCde = "2004/01/02" permet de retrouver le numéro de la commande (2).
  • j1 : Commande.noCde = Contenir.noCde permet de retrouver les lignes de Contenir et,
  • j2 : Contenir.refArt = Article.refArt permet de retrouver la description de chaque article

On obtient bien ancre et manille. CQFD itou. Il y a deux jointures !

3. Combien faire de jointures ?

Simple, il doit y avoir autant de jointures que de virgules dans la liste de table de FROM !

4. Combien de lignes on récupère juste avec une jointure ?

Si on va dans le sens clé étrangère => clé primaire, on récupère une ligne

Si on va dans le sens clé primaire => clé étrangère, on récupère une liste

Applications
A vous de jouer.

Quelle est la liste des articles dans la commande n°1 ? (on a besoin des tables contenir et article)

Quelle est la liste des articles commandés le 01/01/2015 ? (on a besoin des tables commande, contenir et article)

Ouvrir plusieurs fois la même table
Petite colle et renommage.

Parfois, il est nécessaire de renommer des champs, on utilise alors le mot clé AS derrière celui-ci : un comptage, une somme, ou juste comme ça.

On peut faire la même chose avec les tables et c'est parfois très utile.

Exemple

Quels sont les codes et descriptions des articles achetés en même temps que la Manille (mais qui ne sont pas Manille) pour construire la liste du type : "Les autres clients ont aussi acheté ..." ?
On va chercher les articles non Manille contenus dans les mêmes commandes que les commandes qui contiennnent l'article Manille :

Cela se traduit par les n° de commande des enregistrements de Contenir C1 dont la table jointe A1 porte la description "manille",
jointe aux numéros de commande des enregistrement de Contenir C2 dont la table jointe A2 ne poerte pas la description Manille.

SELECT DISTINCT refArt, descArt 
FROM Article A1, Contenir C1, Contenir C2, Article A2
WHERE 
      A1.refArt = C1.refArt AND C2.RefArt = A2.refArt -- 2 jointures, entre les Contenir et les Article
  AND C1.cdeNum = C2.cdeNum    -- la 3ème jointure, LA jointure entre les Contenir C1 et C2
  AND A2.descArt="Manille"     -- on conserve les commandes C2 contenant "Manille"
  AND A1.descArt<>"Manille"    -- on élimine les commandes C1 contenant "Manille"
;

Un petit DISTINCT, pour supprimer les éventuels doublons de la liste.

Vu ?
C'est surtout pratique lorsqu'on a des requêtes imbriquées dans FROM - voir le chapitre concerné, plus loin - .

Bon, OK, c'est pas simple.

Et si on oublie la jointure ?
A voir pour le fun et si vous n'avez toujours pas compris : le produit cartésien = plusieurs tables, pas de jointures.

On obtient un produit cartésien

Le moteur de requête créée une liste où chaque enregistrement de la table 1 est associé avec chaque enregistrement de la table 2.

M'enfin, c'est pas c'qu'on veut!

Il suffit alors de restreindre le résultat aux seules lignes dont la clé étrangère d'une table correspond à la clé primaire de l'autre et le tour est joué.

Exemple sans et avec jointure. C'est édifiant!

  • quelle est la liste des numéros de commandes que le client SAI a passé ?
Le produit cartésien Le résultat attendu
SELECT * 
FROM Client, Commande
SELECT * 
FROM Client, Commande
WHERE Client.id = Commande.idClient
Produit cartésien
idnomClinumCdeidClient
1Air France115
1Air France2357
1Air France315
1Air France415
1Air France357123
1Air France358456
1Air France359456
1Air France360123
15SAI115
15SAI2357
15SAI315
15SAI415
15SAI357123
15SAI358456
15SAI359456
15SAI360123
123Faurecia115
123Faurecia2357
123Faurecia315
123Faurecia415
123Faurecia357123
123Faurecia358456
123Faurecia359456
123Faurecia360123
357Timken115
357Timken2357
357Timken315
357Timken415
357Timken357123
357Timken358456
357Timken359456
357Timken360123
456Brain AG115
456Brain AG2357
456Brain AG315
456Brain AG415
456Brain AG357123
456Brain AG358456
456Brain AG359456
456Brain AG360123

Seules les lignes où
l'identifiant du client (id) est égal à
l'identifiant du client de la commande (idClient)
sont justes !!!

Jointure
idnomClinumCdeidClient
15SAI115
15SAI315
15SAI415
123Faurecia357123
123Faurecia360123
357Timken2357
456Brain AG358456
456Brain AG359456
Pouquoi pas les jointures avec JOIN ?
Utiliser JOIN (prononcer djoïn [ˈdʒɔɪn]) pour faire différentes jointures, le mot clé dédié. Plus facile ? Pas si sûr.

JOIN est un opérateur qui remplace la jointure "standard".

on peut l'utiliser très facilement, mais il faut penser en mode ensembliste.

Utilisation

Une requête avec JOIN est assez simple, au début :

SELECT * 
FROM tableA 
     JOIN tableB ON tableA.champJoint = tableB.champJoint 
WHERE ...

En fait, les jointures ne sont plus dans le WHERE mais immédiatement après chaque table, dans la clause JOIN ... ON (ici)

Ceci permet de distinguer les restrictions normales de celles qui sont des jointures.

La Puissance de JOIN

Elle réside dans le choix de conserver différents types de jointures (et là, on parle d'ensembles)

Soit deux tables A et B, consiérées comme des ensembles de données, de lignes, qui seront jointes selon les modes suivants :

A ∩ B Jointure "normale" ou "intérieure" : que les enregistrements qui correspondent C'est l'intersection entre A et B = jointure normale :
JOIN normal ou son synonyme : INNER JOIN
A - B Jointure gauche : ceux de A (gauche) moins ceux de B On dit aussi la différence A-B
LEFT JOIN
Remarque, il s'agit bien d'une jointure car le résultat contient les colonnes de gauche mais aussi celles de droite. Les enregistrements sans correspondance voient les valeurs de droite misent à NULL.
Un test pourra faire une restriction dans les colonnes de B pour vérifier, /ex., si la saisie à droite est correctement faite (non nulle)
B - A Ceux qui correspondent et ceux de B (celle de droite) sans correspondance dans A L'autre différence : B-A
RIGHT JOIN
A + B Tous les enregistrements FULL JOIN
On retrouve toutes les colonnes avec des valeurs nulles selon qu'il manque des enregistrements à droite ou à gauche.
Attention, ce n'est pas un produit cartésien, la correspondance des enregistrements est conservée si elle existe.
Et il y en a d'autres ...

Rassurez vous, seuls JOIN et Left JOIN nous seront vraiment utiles.

Exemples

1. Jointure normale (intersection)

Le clien SAI à passé des commandes. Quelle est la liste des numéros de ces commandes ?

L'ancienne requête était :

SELECT cdeNum
FROM Commande, Client
WHERE Commande.noCli=Client.noCli -- = jointure entre Commande et Client
AND nomCli="SAI";

 

La nouvelle sera :

SELECT cdeNum
FROM Commande
   JOIN Client ON Commande.noCli = Client.noCli -- = jointure (intersection)
WHERE AND nomCli="SAI";

Peu de différence notable, juste une complexité supplémentaire.

2. Jointure normale à 3 tables

Quelle est la description des articles contenus dans la commande du 02/01/04 ?

L'ancienne requête était :

SELECT descArt
FROM Commande, Contenir, Article
WHERE Commande.noCde=Contenir.noCde -- = jointure 1, entre Commande et Contenir
  AND Contenir.refArt=Article.refArt -- = jointure 2, entre Contenir et Article
  AND dateCde="2004/01/02";

Les jointures sont peu visibles.

 

La nouvelle sera :

SELECT descArt
FROM Commande
     JOIN Contenir ON Commande.noCde = Contenir.noCde -- jointure 1
     JOIN Article  ON Contenir.refArt= Article.refArt -- jointure 2
WHERE dateCde="2004/01/02"

Les jointures bien visibles et distinctes de la restriction (WHERE).

 

2. Jointure gauche (LEFT JOIN)

Si on a des voitures qui sont pilotées 1 pilote et un copilote dans une course. Pour chaque modèle, donner la liste des noms des copilotes

Soit les tables ci-dessous

Liste des voitures
idmodeleidPiloteidCopi
31Mazda rc1123456
34Audi a9123NULL
45Mazda rc4789147
57Mazda rc4789NULL
Liste des pilotes
idnomrole
123Steve Warsonpilote
147Julie Woodco pilote
258Ruth Randsonpilote
369Bob Cramerpilote
456Michel Vaillantco pilote
789Jacky Ickxpilote

Comment obtenir ceci : liste des copilotes de chaque voiture ?

modeleidCopi
Mazda rc1Michel Vaillant
Audi a9 
Mazda rc4Julie Wood
Mazda rc4 

 

On va écrire la géniale requête suivante :

SELECT modele, nom FROM voiture LEFT JOIN personne ON idCopi=persCode

Cette requête va chercher toutes les voitures de la table Voiture et les noms des copilotes pour lesquels l'id du copi de la table voiture correspond à l'id de la personne. Si il n'en n'existe aucun, le nom du copi affiché est NULL.

Toutes les voitures apparaîtront dans la liste