Intermédiaire

Jointures SQL

Les jointures combinent des données provenant de plusieurs tables : nom du client, catégorie du produit, détails de la région, etc.

Rappel : INNER JOIN

INNER JOIN retourne uniquement les lignes qui ont une correspondance dans les deux tables :

sql
SELECT
    v.id,
    v.montant,
    v.date_vente,
    c.nom AS client_nom,
    c.segment
FROM ventes v
INNER JOIN clients c ON v.client_id = c.id
Chargement du diagramme…

Aliases de table

Les alias v et c simplifient l'écriture quand on travaille avec plusieurs tables. Utilisez des alias courts et cohérents : v pour ventes, c pour clients, p pour produits.

Si une vente référence un client_id qui n'existe pas dans la table clients, cette ligne de vente disparaît du résultat. C'est le comportement par défaut d'INNER JOIN.

Que se passe-t-il avec INNER JOIN si une vente a un client_id sans correspondance dans la table clients ?

LEFT JOIN

LEFT JOIN conserve toutes les lignes de la table de gauche, même si elles n'ont pas de correspondance dans la table de droite. Les colonnes manquantes sont remplies avec NULL.

sql
SELECT
    v.id,
    v.montant,
    c.nom AS client_nom,
    c.segment
FROM ventes v
LEFT JOIN clients c ON v.client_id = c.id

Ici, toutes les ventes apparaissent dans le résultat. Si une vente n'a pas de client correspondant, client_nom et segment valent NULL.

TypeLignes retournéesCas d'usage
INNER JOINSeulement les correspondancesEnrichir les données quand la correspondance est garantie
LEFT JOINToutes les lignes de la table de gaucheConserver toutes les données même sans correspondance

En pratique

En BI, on utilise souvent LEFT JOIN pour ne pas perdre de données. Une vente sans client identifié reste pertinente pour le CA global — on ne veut pas la supprimer du résultat.

Jointures multiples

On peut joindre plusieurs tables dans la même requête :

sql
SELECT
    v.id,
    v.montant,
    v.date_vente,
    c.nom AS client_nom,
    c.segment,
    p.nom AS produit_nom,
    p.categorie
FROM ventes v
INNER JOIN clients c ON v.client_id = c.id
INNER JOIN produits p ON v.produit_id = p.id

Chaque JOIN ajoute des colonnes au résultat. L'ordre des jointures n'a pas d'impact sur le résultat (le moteur SQL optimise automatiquement), mais lisez la requête de gauche à droite : on part des ventes, on enrichit avec les clients, puis avec les produits.

Quelle est la différence entre INNER JOIN et LEFT JOIN ?

Combiner JOIN, CTEs et window functions

Exemple : « Classement des segments clients par CA, avec le détail produit. »

sql
WITH ventes_enrichies AS (
    SELECT
        v.montant,
        v.date_vente,
        c.segment,
        p.categorie
    FROM ventes v
    INNER JOIN clients c ON v.client_id = c.id
    INNER JOIN produits p ON v.produit_id = p.id
),
ca_par_segment AS (
    SELECT
        segment,
        SUM(montant) AS chiffre_affaires
    FROM ventes_enrichies
    GROUP BY segment
),
avec_rang AS (
    SELECT
        segment,
        chiffre_affaires,
        RANK() OVER (ORDER BY chiffre_affaires DESC) AS rang
    FROM ca_par_segment
)
SELECT segment, chiffre_affaires, rang
FROM avec_rang
ORDER BY rang

Le flux logique :

Chargement du diagramme…
  1. CTE 1 — ventes_enrichies : jointures pour assembler les données de trois tables
  2. CTE 2 — ca_par_segment : agrégation par segment client
  3. CTE 3 — avec_rang : classement avec RANK()
  4. SELECT final : résultat ordonné

Les CTEs comme pipeline

Les CTEs transforment une requête complexe en pipeline lisible. Chaque CTE est une étape nommée : enrichissement (JOIN), agrégation (GROUP BY), calcul analytique (window function).

Exemple combiné avec LAG

On peut aussi combiner jointures et LAG pour une analyse temporelle enrichie :

sql
WITH ventes_enrichies AS (
    SELECT
        strftime('%Y-%m', v.date_vente) AS mois,
        c.segment,
        v.montant
    FROM ventes v
    INNER JOIN clients c ON v.client_id = c.id
),
ca_mensuel_segment AS (
    SELECT
        mois,
        segment,
        SUM(montant) AS chiffre_affaires
    FROM ventes_enrichies
    GROUP BY mois, segment
),
avec_evolution AS (
    SELECT
        mois,
        segment,
        chiffre_affaires,
        LAG(chiffre_affaires, 1, 0) OVER (
            PARTITION BY segment
            ORDER BY mois
        ) AS ca_precedent
    FROM ca_mensuel_segment
)
SELECT
    mois,
    segment,
    chiffre_affaires,
    chiffre_affaires - ca_precedent AS evolution
FROM avec_evolution
ORDER BY mois, segment

Cette requête combine JOIN (enrichissement), GROUP BY (agrégation) et LAG avec PARTITION BY (évolution par segment), structurés en CTEs.

À retenir

Points clés

  • INNER JOIN : seulement les correspondances — utiliser quand la relation est garantie
  • LEFT JOIN : conserve la table de gauche — utiliser en BI pour ne pas perdre de données
  • Les aliases de table (v, c, p) sont indispensables avec les jointures multiples
  • La combinaison JOIN + CTE + window function couvre la plupart des besoins analytiques
  • Organisez la requête en pipeline de CTEs : enrichissement → agrégation → calcul analytique