CodeGym /Cours /SQL SELF /CTE vs sous-requêtes : quand choisir quoi ?

CTE vs sous-requêtes : quand choisir quoi ?

SQL SELF
Niveau 28 , Leçon 1
Disponible

On sait déjà que les CTE rendent le code plus lisible. Mais est-ce qu’il faut toujours les utiliser ? Parfois, une simple sous-requête fait le boulot mieux et plus vite. On va voir dans quels cas chaque outil est le plus adapté, et apprendre à faire un choix réfléchi.

Sous-requêtes : rapide et simple

Tu te souviens sûrement qu’une sous-requête, c’est du SQL dans du SQL. Elle s’insère direct dans la requête principale et s’exécute “sur place”. Parfait pour des opérations simples et ponctuelles :

-- Trouver les produits plus chers que le prix moyen
SELECT product_name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);

Ici, la sous-requête calcule le prix moyen une seule fois, et basta. Pas besoin de constructions en plus.

Performance : qui va plus vite ?

Les sous-requêtes sont souvent plus rapides pour les opérations simples. PostgreSQL peut les optimiser “à la volée”, surtout quand la sous-requête s’exécute une seule fois :

-- Rapide : la sous-requête s’exécute une seule fois
SELECT customer_id, order_total
FROM orders
WHERE order_date = (SELECT MAX(order_date) FROM orders);

Les CTE sont matérialisés par défaut — PostgreSQL calcule d’abord le résultat du CTE, le stocke comme une table temporaire, puis l’utilise. Ça peut ralentir les requêtes simples :

-- Plus lent : le CTE est matérialisé dans une table temporaire
WITH latest_date AS (
    SELECT MAX(order_date) AS max_date FROM orders
)
SELECT customer_id, order_total
FROM orders, latest_date
WHERE order_date = max_date;

Mais ! Depuis PostgreSQL 12, tu peux gérer la matérialisation :

-- Forcer à NE PAS matérialiser
WITH latest_date AS NOT MATERIALIZED (
    SELECT MAX(order_date) AS max_date FROM orders
)
SELECT customer_id, order_total
FROM orders, latest_date
WHERE order_date = max_date;

Réutilisation multiple : ici les CTE sont rois

Quand tu as besoin plusieurs fois du même résultat intermédiaire, les CTE deviennent indispensables :

-- Avec une sous-requête : on répète la même logique deux fois
SELECT
    (SELECT COUNT(*) FROM orders WHERE status = 'terminée') AS commandes_terminees,
    (SELECT COUNT(*) FROM orders WHERE status = 'terminée') * 100.0 / COUNT(*) AS taux_terminaison
FROM orders;

-- Avec un CTE : on calcule une fois, on utilise deux fois
WITH commandes_terminees AS (
    SELECT COUNT(*) AS count FROM orders WHERE status = 'terminée'
)
SELECT
    co.count AS commandes_terminees,
    co.count * 100.0 / (SELECT COUNT(*) FROM orders) AS taux_terminaison
FROM commandes_terminees co;

Analytique complexe : CTE gagne aux points

Pour de l’analytique en plusieurs étapes, les CTE transforment le chaos en ordre. Compare un rapport de ventes :

Avec des sous-requêtes (c’est le bazar) :

SELECT 
    category,
    revenue,
    revenue * 100.0 / (
        SELECT SUM(p.price * oi.quantity)
        FROM order_items oi
        JOIN products p ON oi.product_id = p.product_id
        JOIN orders o ON oi.order_id = o.order_id
        WHERE EXTRACT(year FROM o.order_date) = 2024
    ) AS part_revenue
FROM (
    SELECT 
        p.category,
        SUM(p.price * oi.quantity) AS revenue
    FROM order_items oi
    JOIN products p ON oi.product_id = p.product_id
    JOIN orders o ON oi.order_id = o.order_id
    WHERE EXTRACT(year FROM o.order_date) = 2024
    GROUP BY p.category
) revenus_par_categorie;

Avec des CTE (tout est rangé) :

WITH ventes_annuelles AS (
    SELECT 
        p.category,
        p.price * oi.quantity AS montant_vente
    FROM order_items oi
    JOIN products p ON oi.product_id = p.product_id
    JOIN orders o ON oi.order_id = o.order_id
    WHERE EXTRACT(year FROM o.order_date) = 2024
),
revenus_par_categorie AS (
    SELECT 
        category,
        SUM(montant_vente) AS revenue
    FROM ventes_annuelles
    GROUP BY category
),
revenu_total AS (
    SELECT SUM(montant_vente) AS total FROM ventes_annuelles
)
SELECT 
    rc.category,
    rc.revenue,
    rc.revenue * 100.0 / rt.total AS part_revenue
FROM revenus_par_categorie rc, revenu_total rt;

Récursivité : monopole des CTE

Pour les structures hiérarchiques, les sous-requêtes sont impuissantes.

Seuls les CTE récursifs gèrent des trucs comme “trouver tous les subordonnés d’un manager” :

WITH RECURSIVE hierarchie_employes AS (
    -- On commence par le CEO
    SELECT employee_id, manager_id, name, 1 AS niveau
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- On ajoute les subordonnés de chaque niveau
    SELECT e.employee_id, e.manager_id, e.name, he.niveau + 1
    FROM employees e
    JOIN hierarchie_employes he ON e.manager_id = he.employee_id
)
SELECT * FROM hierarchie_employes ORDER BY niveau, name;

Debug et maintenance du code

Les CTE sont faciles à debugger étape par étape :

-- On vérifie la première étape
WITH clients_actifs AS (
    SELECT customer_id FROM customers WHERE status = 'actif'
)
SELECT COUNT(*) FROM clients_actifs; -- On s’assure que la logique est bonne

-- On ajoute la deuxième étape
WITH clients_actifs AS (...),
commandes_recentes AS (
    SELECT customer_id, COUNT(*) as nb_commandes
    FROM orders
    WHERE order_date >= '2024-01-01'
    GROUP BY customer_id
)
SELECT COUNT(*) FROM commandes_recentes; -- On vérifie aussi cette étape

Les sous-requêtes sont plus galères à debugger — il faut les sortir de leur contexte.

Conseils pratiques

Utilise les sous-requêtes quand :

  • La logique est simple et tient sur une ligne
  • Tu veux les meilleures perfs pour des opérations simples
  • Le résultat intermédiaire ne sert qu’une fois
  • Tu bosses sur des petits volumes de données

Utilise les CTE quand :

  • La requête est complexe et se découpe en étapes logiques
  • Tu dois réutiliser plusieurs fois des résultats intermédiaires
  • La lisibilité et la maintenabilité du code sont importantes
  • Tu bosses avec des hiérarchies (CTE récursifs)
  • Tu débugges une logique complexe étape par étape

La règle d’or

Commence par une sous-requête. Si ça devient illisible ou que la logique se répète — passe aux CTE. Ton futur collègue (ou toi-même dans six mois) te dira merci !

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION