ParcoursOuvrir l'appli

Relier les tables : JOIN

SQL et bases de données · 55 min

Pourquoi plusieurs tables ?

Dans la table ventes, le nom Paul est écrit 17 fois et riz 15 fois. Que se passe-t-il si :

La solution des bases relationnelles : une table par sujet, reliées par des numéros.

produits                          vendeurs
id | nom   | prix_achat | ...     id | nom      | telephone
 1 | riz   | 1000       |          1 | Awa      | 690112233
 2 | huile | 1250       |          2 | Paul     | 677445566

ventes
id | date       | produit_id | vendeur_id | quantite
 1 | 2026-01-01 | 1          | 1          | 5
 2 | 2026-01-02 | 2          | 2          | 10

La vente n°2 ne dit plus « huile, Paul » : elle dit « produit 2, vendeur 2 ». Le nom de Paul n'est écrit qu'une seule fois, dans la table vendeurs.

La nouvelle base, boutique2.sql, a aussi deux informations qu'on n'avait pas : le prix d'achat de chaque produit, qui permet enfin de calculer le bénéfice, et un nouveau vendeur, Junior, embauché il y a peu.

JOIN : recoller les morceaux

SELECT ventes.date, produits.nom, ventes.quantite
FROM ventes
JOIN produits ON ventes.produit_id = produits.id;

Lis-le ainsi : « pour chaque vente, va chercher la ligne de produits dont l'id est égal au produit_id de la vente, et colle-les ensemble ».

Quand deux tables ont des colonnes du même nom (id, nom), on précise la table : produits.nom, vendeurs.nom. Pour écrire moins, on donne un alias court à chaque table :

SELECT v.date, p.nom, d.nom
FROM ventes v
JOIN produits p ON v.produit_id = p.id
JOIN vendeurs d ON v.vendeur_id = d.id;

On peut enchaîner autant de JOIN que nécessaire.

Le bénéfice

SELECT p.nom, SUM(v.quantite * (p.prix_vente - p.prix_achat)) AS benefice
FROM ventes v
JOIN produits p ON v.produit_id = p.id
GROUP BY p.nom
ORDER BY benefice DESC;

Exercice : Recoller ventes, produits et vendeurs

Dans boutique2.sql, écris une requête qui donne, pour les 3 premières ventes (dans l'ordre de leur id), la date, le nom du produit et le nom du vendeur.

Voir une solution
import sqlite3

conn = sqlite3.connect(":memory:")
conn.executescript(open("boutique2.sql", encoding="utf-8").read())

requete = """
SELECT v.date, p.nom, d.nom
FROM ventes v
JOIN produits p ON v.produit_id = p.id
JOIN vendeurs d ON v.vendeur_id = d.id
ORDER BY v.id
LIMIT 3
"""
resultat = conn.execute(requete).fetchall()
for ligne in resultat:
    print(ligne)

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

Exercice : Ce que chaque produit rapporte vraiment

Écris une requête qui donne, pour chaque produit vendu, son nom et son bénéfice total (quantité x marge), du plus rentable au moins rentable.

Voir une solution
import sqlite3

conn = sqlite3.connect(":memory:")
conn.executescript(open("boutique2.sql", encoding="utf-8").read())

requete = """
SELECT p.nom, SUM(v.quantite * (p.prix_vente - p.prix_achat)) AS benefice
FROM ventes v
JOIN produits p ON v.produit_id = p.id
GROUP BY p.nom
ORDER BY benefice DESC
"""
resultat = conn.execute(requete).fetchall()
for ligne in resultat:
    print(ligne)

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

LEFT JOIN : ce qui n'a pas de correspondance

Junior n'a encore rien vendu. Et le sel est dans le catalogue, mais personne n'en a acheté. Avec un JOIN ordinaire, ils disparaissent du résultat : il n'y a aucune vente à coller à côté d'eux.

SELECT d.nom, v.id
FROM vendeurs d
LEFT JOIN ventes v ON v.vendeur_id = d.id
WHERE v.id IS NULL;

« Tous les vendeurs, avec leurs ventes s'ils en ont ; garde ceux qui n'en ont aucune. » Résultat : Junior. C'est la requête classique pour trouver les clients inactifs, les produits invendus, les élèves sans note.

Exercice : Les produits jamais vendus

Écris une requête qui donne le nom des produits du catalogue qui n'ont jamais été vendus.

Voir une solution
import sqlite3

conn = sqlite3.connect(":memory:")
conn.executescript(open("boutique2.sql", encoding="utf-8").read())

requete = """
SELECT p.nom
FROM produits p
LEFT JOIN ventes v ON v.produit_id = p.id
WHERE v.id IS NULL
"""
resultat = conn.execute(requete).fetchall()
for ligne in resultat:
    print(ligne)

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice