Apprenez à pivoter des données en SQL avec CASE WHEN, FILTER et extensions natives. Exemples chiffrés, comparatifs et exercices pour l'entretien.
Thomas Leroy
Pivoter des données en SQL, c'est transformer des lignes en colonnes pour produire un tableau croisé lisible — une opération que tout data analyst rencontre tôt ou tard en entretien ou en production.
Contrairement à Excel où un tableau croisé dynamique se fait en 3 clics, SQL exige de comprendre la logique sous-jacente. La bonne nouvelle : une fois le mécanisme assimilé, vous pouvez pivoter n'importe quel jeu de données avec élégance, quelle que soit la base de données utilisée.
Dans ce guide, vous allez découvrir toutes les techniques de pivot SQL — de la méthode manuelle avec CASE WHEN aux extensions natives comme PIVOT (SQL Server, Oracle) ou crosstab (PostgreSQL) — avec des exemples chiffrés, des tableaux comparatifs et les pièges à éviter en entretien.
📌 Ce qu'il faut retenir
Le pivot SQL consiste à transformer des valeurs d'une colonne en en-têtes de colonnes distinctes
La méthode universelle repose sur CASE WHEN combiné à GROUP BY et une fonction d'agrégat
SQL Server et Oracle proposent un opérateur PIVOT natif ; PostgreSQL utilise crosstab
Le pivot dynamique (colonnes inconnues à l'avance) nécessite du SQL dynamique ou du code applicatif
Comprendre le problème : données longues vs données larges
En analyse de données, on distingue 2 formats fondamentaux :
Format long (long format) : chaque ligne représente une observation unique. Une colonne categorie contient les libellés, une colonne valeur contient les chiffres.
Format large (wide format) : chaque catégorie occupe sa propre colonne. C'est le format attendu pour un rapport ou un tableau de bord.
Prenons un exemple concret. Une table ventes contient les revenus mensuels par région :
C'est exactement ce que le pivot SQL permet d'obtenir.
Méthode universelle : CASE WHEN + GROUP BY
La technique la plus portable — elle fonctionne sur tous les moteurs SQL — consiste à combiner CASE WHEN avec une fonction d'agrégat et un GROUP BY.
Syntaxe de base
SELECT
region,
SUM(CASEWHEN mois ='2025-01'THEN revenu ELSE0END) AS "2025-01",
SUM(CASEWHEN mois ='2025-02'THEN revenu ELSE0END) AS "2025-02"
FROM ventes
GROUPBY region
ORDERBY region;
Le principe est simple : pour chaque colonne cible, on crée une expression CASE WHEN qui renvoie la valeur lorsque la condition est vraie, et 0 (ou NULL) sinon. Le SUM — ou MAX, AVG selon le besoin — agrège ensuite les valeurs par groupe.
Choisir entre SUM, MAX et COUNT
Le choix de la fonction d'agrégat est crucial et dépend de la sémantique de vos données :
Fonction
Quand l'utiliser
Exemple typique
SUM
Additionner des montants ou quantités
Revenu total par région et mois
MAX
Récupérer une valeur unique (sans doublons)
Dernier statut d'une commande
COUNT
Compter des occurrences
Nombre de commandes par catégorie
AVG
Calculer une moyenne sur plusieurs lignes
Note moyenne par produit et trimestre
MIN
Récupérer la valeur minimale
Prix plancher par fournisseur et mois
💡 Bon à savoir
Utilisez MAX plutôt que SUM quand vous êtes certain qu'il ne peut exister qu'une seule ligne par combinaison (région × mois). Cela évite de cumuler des doublons accidentels et rend l'intention plus lisible.
Exemple complet : tickets de support par statut
Imaginons une table tickets avec les colonnes agent, statut (ouvert, resolu, en_cours) et nb_tickets. On veut un rapport croisé agents × statuts :
SELECT
agent,
SUM(CASEWHEN statut ='ouvert'THEN nb_tickets ELSE0END) AS ouvert,
SUM(CASEWHEN statut ='en_cours'THEN nb_tickets ELSE0END) AS en_cours,
SUM(CASEWHEN statut ='resolu'THEN nb_tickets ELSE0END) AS resolu,
SUM(nb_tickets) AS total
FROM tickets
GROUPBY agent
ORDERBY total DESC;
L'ajout de la colonne total est une bonne pratique : elle permet de vérifier immédiatement la cohérence du pivot (la somme des colonnes pivotées doit égaler total).
La clause FILTER : une alternative plus lisible (PostgreSQL)
PostgreSQL 9.4+ propose la clause FILTER qui rend le pivot plus expressif et légèrement plus performant :
SELECT
agent,
SUM(nb_tickets) FILTER (WHERE statut ='ouvert') AS ouvert,
SUM(nb_tickets) FILTER (WHERE statut ='en_cours') AS en_cours,
SUM(nb_tickets) FILTER (WHERE statut ='resolu') AS resolu
FROM tickets
GROUPBY agent;
La syntaxe aggregat(...) FILTER (WHERE condition) remplace avantageusement le CASE WHEN ... ELSE 0. Le résultat est strictement identique, mais le code est plus facile à lire et à maintenir quand le nombre de colonnes pivotées augmente.
💡 Bon à savoir
La clause FILTER est reconnue dans la norme SQL:2003. Elle est supportée par PostgreSQL, DuckDB et certaines versions récentes de SQLite. En revanche, MySQL, SQL Server et Oracle ne la supportent pas — restez sur CASE WHEN pour du code portable.
L'opérateur PIVOT natif de SQL Server et Oracle
SQL Server (depuis 2005) et Oracle (depuis 11g) disposent d'un opérateur PIVOT intégré qui simplifie la syntaxe pour les cas statiques.
SQL Server : syntaxe PIVOT
SELECT region, [2025-01], [2025-02]
FROM (
SELECT region, mois, revenu
FROM ventes
) AS source
PIVOT (
SUM(revenu)
FOR mois IN ([2025-01], [2025-02])
) AS pivot_result
ORDERBY region;
La sous-requête source prépare les données ; PIVOT effectue la rotation en spécifiant la fonction d'agrégat, la colonne à pivoter (FOR mois) et les valeurs cibles (IN ([2025-01], [2025-02])).
Oracle : même logique, légères différences
SELECT*FROM ventes
PIVOT (
SUM(revenu)
FOR mois IN ('2025-01'AS "jan_2025", '2025-02'AS "fev_2025")
)
ORDERBY region;
Oracle permet d'aliaser directement les valeurs dans la clause IN, ce qui évite les noms de colonnes numériques awkward.
Comparaison des approches par moteur SQL
Moteur
Méthode native
CASE WHEN
FILTER
Pivot dynamique
PostgreSQL
crosstab() (extension tablefunc)
✅
✅
SQL dynamique
SQL Server
PIVOT natif
✅
❌
SQL dynamique
Oracle
PIVOT natif
✅
❌
XML PIVOT
MySQL / MariaDB
Aucune
✅
❌
SQL dynamique
BigQuery
PIVOT natif (2021+)
✅
❌
Procédures
DuckDB
PIVOT natif
✅
✅
PIVOT auto
PostgreSQL crosstab : la fonction tablefunc
PostgreSQL propose la fonction crosstab() via l'extension tablefunc. Elle est plus performante sur de gros volumes mais nécessite une configuration préalable :
-- Activer l'extension (une seule fois par base)CREATE EXTENSION IF NOTEXISTS tablefunc;
-- UtilisationSELECT*FROM crosstab(
'SELECT region, mois, SUM(revenu)
FROM ventes
GROUP BY region, mois
ORDER BY 1, 2',
'VALUES (''2025-01''), (''2025-02'')'
) AS ct(region TEXT, "jan_2025" NUMERIC, "fev_2025" NUMERIC);
La fonction crosstab prend 2 requêtes en argument : la première renvoie les données source triées, la seconde liste les valeurs de pivot attendues. Le typage explicite des colonnes de sortie est obligatoire.
⚠️ Attention
crosstab() est sensible à l'ordre des lignes. Si votre requête source n'est pas triée par (row_identifier, category) dans le bon ordre, les valeurs seront décalées sans qu'aucune erreur ne soit levée. Testez toujours avec des données de référence connues.
Pivot dynamique : quand les colonnes ne sont pas connues à l'avance
Le défi du pivot dynamique survient lorsque les valeurs à pivoter (les mois, les catégories, les statuts) varient et ne sont pas connues au moment de l'écriture de la requête.
Approche en SQL Server avec SQL dynamique
DECLARE@colonnes NVARCHAR(MAX);
DECLARE@sql NVARCHAR(MAX);
-- Récupérer dynamiquement la liste des moisSELECT@colonnes= STRING_AGG(QUOTENAME(mois), ', ')
FROM (SELECTDISTINCT mois FROM ventes) AS mois_distincts;
-- Construire et exécuter la requête PIVOTSET@sql= N'
SELECT region, '+@colonnes+'
FROM (SELECT region, mois, revenu FROM ventes) src
PIVOT (SUM(revenu) FOR mois IN ('+@colonnes+')) pvt
ORDER BY region;';
EXEC sp_executesql @sql;
Cette technique est puissante mais comporte des risques : injection SQL si les données proviennent d'une source non maîtrisée, et difficulté à typer les colonnes de sortie statiquement. Dans la majorité des projets data réels, le pivot dynamique est délégué à la couche applicatrice (Python pandas, dbt, etc.).
Unpivot : l'opération inverse
L'opération inverse du pivot s'appelle unpivot : elle transforme des colonnes en lignes. C'est utile pour normaliser une table "wide" reçue d'une source externe.
Unpivot avec CROSS JOIN LATERAL (PostgreSQL)
SELECT region, mois, revenu
FROM ventes_wide
CROSSJOINLATERAL (
VALUES ('2025-01', "2025-01"),
('2025-02', "2025-02")
) AS t(mois, revenu)
ORDERBY region, mois;
SQL Server dispose d'un opérateur UNPIVOT natif avec une syntaxe symétrique à PIVOT. PostgreSQL favorise CROSS JOIN LATERAL ou unnest.
Pour aller plus loin sur les transformations de données, consultez notre guide sur les opérateurs ensemblistes SQL : UNION, INTERSECT, EXCEPT qui permettent de combiner des résultats pivotés issus de plusieurs sources.
Pivot SQL en entretien technique : ce que les recruteurs évaluent
Le pivot SQL est un sujet récurrent dans les entretiens data analyst et data engineer. Voici ce qui est généralement évalué :
La maîtrise de la méthode universelle (CASE WHEN + GROUP BY) — attendue de tout candidat
La connaissance des extensions natives du moteur cible (SQL Server PIVOT, PostgreSQL crosstab)
La gestion des NULLs — savoir quand utiliser COALESCE(valeur, 0) pour éviter les NULLs dans les colonnes pivotées
La performance — comprendre pourquoi un pivot sur 100 millions de lignes nécessite une réflexion sur les index et la cardinalité
L'approche dynamique — expliquer pourquoi on l'évite en SQL pur et comment on le délègue
Les questions d'entretien sur le pivot s'articulent souvent avec les fonctions fenêtre SQL, car les deux compétences sont fréquemment combinées dans les exercices de reporting avancé.
💡 Bon à savoir
En entretien, si le moteur n'est pas précisé, proposez systématiquement la solution CASE WHEN en première approche, puis mentionnez l'existence des opérateurs natifs selon le SGBD. Cela montre à la fois votre maîtrise des fondamentaux et votre connaissance des spécificités des outils.
Gestion des NULLs et valeurs manquantes dans un pivot
Un pivot produit naturellement des NULL pour les combinaisons qui n'existent pas dans les données source. Par exemple, si la région "Est" n'a aucune vente en février, la colonne 2025-02 sera NULL pour cette ligne.
Pour traiter ces cas, enveloppez vos expressions dans COALESCE :
SELECT
region,
COALESCE(SUM(CASEWHEN mois ='2025-01'THEN revenu END), 0) AS "2025-01",
COALESCE(SUM(CASEWHEN mois ='2025-02'THEN revenu END), 0) AS "2025-02"
FROM ventes
GROUPBY region;
La différence entre ELSE 0 et COALESCE(..., 0) est subtile mais importante :
CASE WHEN ... ELSE 0 END renvoie 0 dès que la condition n'est pas remplie — même si la ligne existe
CASE WHEN ... THEN valeur END (sans ELSE) renvoie NULL si la condition est fausse, et COALESCE le remplace ensuite par 0
Les 2 approches donnent le même résultat dans la majorité des cas, mais la seconde est plus explicite sur l'intention sémantique : "il n'y a pas de données" versus "la valeur est zéro".
Optimiser les performances d'un pivot sur grand volume
Sur des tables de plusieurs dizaines de millions de lignes, un pivot mal écrit peut être catastrophiquement lent. Voici les leviers d'optimisation :
Filtrer avant de pivoter : appliquez les clauses WHERE dans une CTE ou sous-requête avant la rotation, pas après
Indexer la colonne pivotée : créez un index sur la colonne dont les valeurs deviennent des en-têtes (ici mois)
Matérialiser les agrégats intermédiaires : si le pivot est exécuté fréquemment, pré-agréger les données dans une vue matérialisée ou une table de staging
Limiter le nombre de colonnes : au-delà de 20-30 colonnes pivotées, envisagez une représentation alternative (JSON, tableau croisé côté application)
Quelle est la différence entre PIVOT et CASE WHEN pour pivoter des données ?
CASE WHEN est une approche universelle qui fonctionne sur tous les moteurs SQL. L'opérateur PIVOT (SQL Server, Oracle, BigQuery, DuckDB) est une syntaxe native plus concise, mais qui produit le même résultat. En entretien, maîtriser CASE WHEN est indispensable ; connaître PIVOT est un plus apprécié.
Comment pivoter des données quand les colonnes ne sont pas connues à l'avance ?
C'est le cas du pivot dynamique. En SQL pur, il faut construire la requête en chaîne de caractères et l'exécuter via EXEC (SQL Server) ou EXECUTE (PostgreSQL). En pratique, cette logique est souvent déplacée côté application (Python, dbt) pour plus de lisibilité et de sécurité.
Le pivot SQL est-il différent d'un tableau croisé dynamique Excel ?
Fonctionnellement, oui c'est équivalent — les 2 transforment des lignes en colonnes agrégées. La différence est que le pivot SQL est déclaratif et reproductible : la requête peut être versionnée, testée et exécutée à grande échelle. Un tableau croisé Excel dépend d'une interface graphique et ne se prête pas à l'automatisation.
Peut-on pivoter sur plusieurs colonnes simultanément ?
Oui, mais cela complexifie considérablement la requête. Il faut concaténer les valeurs des 2 colonnes pour créer des identifiants uniques, puis pivoter sur ces identifiants composites. Par exemple, region || '_' || mois comme valeur à pivoter. Au-delà de 2 dimensions, envisagez un modèle OLAP ou un outil de BI dédié.
Comment tester qu'un pivot est correct ?
La meilleure vérification est de comparer la somme totale avant et après le pivot. Avant : SELECT SUM(revenu) FROM ventes doit être égale à la somme de toutes les cellules du tableau pivoté. Un écart signale un doublon, un NULL non géré ou une erreur de condition dans le CASE WHEN.
Le pivot affecte-t-il les performances de façon significative ?
Oui, un pivot implique un GROUP BY complet sur la table source. Sur des volumes importants (10M+ lignes), assurez-vous que la colonne pivotée est indexée et que les filtres WHERE sont appliqués en amont. Une vue matérialisée peut réduire drastiquement le temps de réponse pour les rapports récurrents.
BigQuery supporte-t-il l'opérateur PIVOT natif ?
Oui, depuis 2021, BigQuery supporte la syntaxe PIVOT avec une syntaxe similaire à SQL Server. DuckDB — moteur analytique en plein essor — va encore plus loin avec un PIVOT automatique qui déduit les colonnes sans les lister explicitement, ce qui est idéal pour l'exploration rapide de données.
Conclusion
Le pivot SQL est une compétence fondamentale pour tout data analyst ou data engineer qui produit des rapports ou prépare des données pour la visualisation. La maîtrise de la méthode CASE WHEN + GROUP BY vous garantit une solution portable sur tous les moteurs ; la connaissance des opérateurs natifs (PIVOT, crosstab, FILTER) vous permet d'écrire du code plus expressif sur les plateformes qui les supportent.
Retenez les 3 points essentiels : filtrez vos données avant de pivoter, gérez les NULL avec COALESCE, et déléguez le pivot dynamique à la couche applicative. Avec ces principes, vous répondrez à n'importe quelle question de pivot en entretien avec clarté et précision.
Envie de pratiquer ? Retrouvez des exercices SQL interactifs sur le pivot, les fonctions fenêtre et bien d'autres sujets directement sur SQL Pratique.
Prêt à vous entraîner ?
50 exercices SQL interactifs avec éditeur en ligne, chronomètre et feedback IA.