10.Les jointures

10.1.Introduction

Considérons cette fois-ci une base de données gérant une discothèque. En première approximation nous pourrions envisager de créer une table via la requête
CREATE TABLE chansons (nom VARCHAR(64), interprete VARCHAR(64));
avec un contenu similaire à
nom interprete
Les feuilles mortes Yves Montand
Tous les cris les SOS Daniel Balavoine
Je ne suis pas un héros Daniel Balavoine
Le problème c'est que cela implique de recopier moult fois les mêmes informations (ici le nom complet de l'artiste). Qui plus est, selon le mode de saisie choisi, des erreurs peuvent intervenir et faire que le même artiste soit orthographié de différents façons pour diverses chansons. Dans ces conditions il sera impossible de retrouver avec certitude l'ensemble des chansons d'un interprête (une requête WHERE interprete='Daniel Balavoine' ne permettra pas de retrouver des chansons attribués à 'Balavoine Daniel').
Plus subtil, l'occupation disque n'est pas optimisé puisque l'on va retrouver N fois la même chaîne de caractères.
Il est préférable de créer une table d'interprètes dans laquelle chacun sera identifié par un nombre (occupant bien moins de place qu'une chaîne de 64 caractères - comme dans notre exemple - ou plus). Au passage on en profitera pour distinguer les champs nom et prénom:
id nom prenom
1 Montand Yves
2 Balavoine Daniel
Maintenant que nous avons cette table d'interprètes la table précédente peut être modifiée en:
nom interprete_id
Les feuilles mortes 1
Tous les cris les SOS 2
Je ne suis pas un héros 2
Elle aura alors été créé par une requête du type
CREATE TABLE chansons (nom VARCHAR(64), interprete_id INTEGER UNSIGNED);
Nous avons maintenant 2 tables. La table 'chansons' contient un champ 'interprete_id' qui fait référence au champ 'id' de la table 'interpretes'. Les 2 tables sont liées.
rem
  • L'occupation disque (max) de la table initiale était de 3*(64+64) octets (3 lignes de 2 champs de 64 caractères sur 1 octet). L'occupation disque (max) des tables liées est maintenant de 2*(4+64) octets (2 lignes composées d'un entier sur 4 octets et d'une chaine sur 64 caractères si l'on considère toujours le cas où nom et prénom sont ensembles) + 3*(64+4) octets (3 lignes de 64 caractères et 4 octets pour l'entier). Nous sommes donc passés (si nous n'avions conservé qu'un champ pour nom+prénom) de 384 octets à 340 octets soit un gain de place évident.
  • Il est maintenant possible de modifier/ajouter des données sur les interprètes sans se soucier du contenu de la table 'chansons' (à partir du moment où l'identifiant 'id' n'est pas modifié)
  • Si la liste des choix possibles pour l'interprète avait été restreint (disons au maximum une dizaine de valeurs possibles) nous aurions pu envisager d'utiliser un champ de type ENUM
C'est bien beau tout cela mais maintenant il n'est plus possible de retrouver la liste des chansons interprétées par un chanteur donné juste à partir de son nom et de la table 'chansons' comme nous le faisions avant. Pour réaliser ce type de requête, il faut relier les tables par des jointures comme nous allons le voir immédiatement.

10.2.Les jointures

Pour exécuter une requête réalisant une jointure "classique" (nous verrons par la suite qu'il en existe de différents types) il suffit de faire référence aux 2 tables lors des requêtes SELECT et de les associer avec la condition WHERE adéquate, comme suit:
SELECT * FROM chansons,interpretes WHERE chansons.interprete_id=interpretes.id
Indiquant ainsi que le champ 'id' de la table 'interpretes' doit coïncider avec le champ 'interprete_id' de la table 'chansons'.
rem
  • Comme, dans notre cas, il ne peut y avoir de confusion ('interprete_id' ne peut qu'être un champ de la table 'chansons' et 'id' un champ de la table 'interprete') nous aurions également pu écrire
    SELECT * FROM chansons,interpretes WHERE interprete_id=id
Ceci retourna alors
nom interprete_id id nom prenom
Les feuilles mortes 1 1 Montand Yves
Tous les cris les SOS 2 2 Balavoine Daniel
Je ne suis pas un héros 2 2 Balavoine Daniel
Bref, plus ou moins l'équivalent de la table que nous avions initialement créée.
Il est alors facile de lister les chansons d'un interprète donné en ajoutant, à la requête qui fait la jointure, la condition du filtre:
SELECT * FROM chansons,interpretes WHERE interprete_id=id AND interpretes.nom='Balavoine'
rem
  • Les 2 tables incluant un champ baptisé 'nom' il est nécessaire de préciser sur quelle table le filtre nom=valeur s'applique (en mettant nomtable.nomchamp=valeur)
Pour pouvoir plus facilement interpréter le résultat retourné par la requête, il peut être intéressant d'utiliser un alias via le mot clé AS pour distinguer les 2 champs 'nom'. Comme avec la requête
SELECT chansons.nom AS chanson,interprete.nom,prenom FROM chansons,interpretes WHERE interprete_id=id
qui retournera alors
chanson nom prenom
Les feuilles mortes Montand Yves
Tous les cris les SOS Balavoine Daniel
Je ne suis pas un héros Balavoine Daniel

10.3.Liens 1 à plusieurs

Dans le chapitre précédent nous avons vu comment associer à un élément d'une table (i.e. 'chansons') un élément d'une autre table (i.e. 'interpretes') (on parle de lien 1 à 1). Cependant ceci ne permet pas de gérer tous les cas de figure. Il est des cas où des éléments d'une table doivent être reliés à plusieurs éléments d'une autre table (on parle alors de lien 1 à plusieurs). C'est par exemple le cas, si vous souhaitez stocker en base, pour chaque film les différents acteurs du casting [REM dans ce cas, il s'agit même d'un lien plusieurs à plusieurs puisque plusieurs films peuvent être associé à un même acteur].
Pour obtenir ce résultat, il n'est évidemment pas question d'avoir une table du genre
nom acteur1_id acteur2_id acteur3_id
La cité de la peur 1 2 3
La grande vadrouille 4 5 6
Le corniaud 4 5 7
Ne serait-ce que parce que ceci ne permet pas de déclarer un nombre indéfini d'acteurs (combien de champs 'acteur' créer? il n'y a pas de réponse à cette question)
Non, la solution passe par la création d'une table intermediaire entre la table contenant les films et la table contenant les acteurs. Une table de jointure.
Table films
id film
1 La cité de la peur
2 La grande vadrouille
3 Le corniaud
Table films_acteurs
film_id acteur_id
1 3
2 1
2 2
3 1
3 2
Table acteurs
id acteur
1 Louis de Funes
2 Bourvil
3 Alain Chabat
A partir de là, la jointure se fait selon le même principe que nous avons vu précédemment.
SELECT * FROM films,acteurs,films_acteurs WHERE films.id=film_id AND acteur_id=acteurs.id 

10.4.Monsieur plus

Voici une introduction aux jointures... qui laisse la place à de nouvelles interrogations:
  • Quels sont les différents types de jointures et quelles sont leurs différences?
  • Comment affecter un identifiant numérique unique à chaque enregistrement d'une table?