đ Module 2 â CTEs (Common Table Expressions)#
đŻ Objectifs du module#
Ă la fin, tu sauras :
- Ăcrire des CTEs pour rendre ton SQL plus lisible
- ChaĂźner des requĂȘtes complexes avec plusieurs Ă©tapes
- Ăcrire une CTE rĂ©cursive (pour hiĂ©rarchie, arborescence, etc.)
- Appliquer ça Ă des cas rĂ©els dâanalyse de donnĂ©es
đ§ C’est quoi une CTE ?#
Une CTE est comme une vue temporaire et nommĂ©e dans une requĂȘte. Elle te permet de :
- Casser une requĂȘte en plusieurs morceaux lisibles
- Réutiliser des sous-ensembles intermédiaires
- Ăviter les sous-requĂȘtes imbriquĂ©es illisibles
đ Syntaxe de base :#
WITH alias AS (
SELECT ...
)
SELECT * FROM alias;
Tu peux en chaĂźner plusieurs :
WITH
step1 AS (SELECT ...),
step2 AS (SELECT * FROM step1 WHERE ...)
SELECT * FROM step2;
đ ïž Dataset de base#
On garde notre dataset de clients et commandes du module précédent.
â Exemple 1 â CTE pour simplifier une agrĂ©gation#
WITH commandes_par_client AS (
SELECT client_id, COUNT(*) AS nb_commandes
FROM commandes
GROUP BY client_id
)
SELECT c.nom, cpc.nb_commandes
FROM clients c
LEFT JOIN commandes_par_client cpc ON c.client_id = cpc.client_id;
đ On sĂ©pare lâagrĂ©gation de la jointure = plus lisible, modulaire.
â Exemple 2 â Plusieurs CTEs#
WITH
commandes_par_client AS (
SELECT client_id, COUNT(*) AS nb_commandes
FROM commandes
GROUP BY client_id
),
clients_avec_commandes AS (
SELECT * FROM clients WHERE client_id IN (SELECT client_id FROM commandes)
)
SELECT cac.nom, cpc.nb_commandes
FROM clients_avec_commandes cac
JOIN commandes_par_client cpc ON cac.client_id = cpc.client_id;
â Exemple 3 â Remplacer une sous-requĂȘte#
-- Moins lisible
SELECT nom
FROM clients
WHERE client_id IN (
SELECT client_id FROM commandes WHERE produit = 'Chaise'
);
-- Mieux avec une CTE
WITH acheteurs_chaise AS (
SELECT client_id FROM commandes WHERE produit = 'Chaise'
)
SELECT c.nom
FROM clients c
JOIN acheteurs_chaise ac ON c.client_id = ac.client_id;
đ CTEs rĂ©cursives : cas typique = hiĂ©rarchies, arbres#
đĄ On utilise le mot-clĂ© spĂ©cial
RECURSIVE
dans leWITH
â Exemple 4 â Trouver tous les subordonnĂ©s dâun manager#
WITH RECURSIVE hierarchie AS (
-- Base : les subordonnés directs d'Alice
SELECT emp_id, nom, manager_id
FROM employes
WHERE manager_id = 1
UNION ALL
-- Récursivité : les subordonnés de ceux déjà trouvés
SELECT e.emp_id, e.nom, e.manager_id
FROM employes e
JOIN hierarchie h ON e.manager_id = h.emp_id
)
SELECT * FROM hierarchie;
đ â Retourne tous les descendants directs et indirects dâAlice (emp_id = 1)
â ïž PiĂšges frĂ©quents#
Erreur | Cause | Solution |
---|---|---|
CTE non utilisĂ©e | tu as oubliĂ© de lâappeler ensuite | VĂ©rifie que tu appelles bien lâalias |
Pas de récursivité | tu as oublié RECURSIVE | Ajoute WITH RECURSIVE ... |
RĂ©cursion infinie | pas de condition dâarrĂȘt | Assure-toi que les relations sont finies |
Mauvais nommage | mĂȘme nom de colonne en base et CTE | Renomme les colonnes au besoin |
đïžââïž Exercices pratiques#
Exo 1 â Noms des clients qui ont commandĂ© plus dâun produit#
Astuce : CTE pour compter, puis filtre
Exo 2 â Liste des clients sans aucune commande#
Astuce :
LEFT JOIN
+ CTE dâagrĂ©gation
Exo 3 â Liste hiĂ©rarchique des subordonnĂ©s de Bob (emp_id = 2)#
Exo 4 â Pour chaque produit, nombre de clients diffĂ©rents lâayant commandĂ©#
đ§° Bonus â Tester tout ça dans DuckDB#
import duckdb
# Liste des clients qui ont commandé >1 produit
duckdb.sql("""
WITH nb_cmd AS (
SELECT client_id, COUNT(*) AS n
FROM commandes
GROUP BY client_id
)
SELECT c.nom, n
FROM clients c
JOIN nb_cmd ON c.client_id = nb_cmd.client_id
WHERE n > 1;
""").df()