SQL Pratique
Pivot SQL : transformer des lignes en colonnes
14 min de lecture

Pivot SQL : transformer des lignes en colonnes

Apprenez à pivoter des données en SQL avec CASE WHEN, FILTER et extensions natives. Exemples chiffrés, comparatifs et exercices pour l'entretien.

Avatar de Thomas LeroyThomas 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 :

| region    | mois       | revenu  |
|-----------|------------|---------|
| Nord      | 2025-01    | 42 000  |
| Nord      | 2025-02    | 38 500  |
| Sud       | 2025-01    | 61 200  |
| Sud       | 2025-02    | 57 800  |
| Est       | 2025-01    | 29 400  |
| Est       | 2025-02    | 33 100  |

Le résultat souhaité après pivot :

| region | 2025-01 | 2025-02 |
|--------|---------|---------|
| Est    | 29 400  | 33 100  |
| Nord   | 42 000  | 38 500  |
| Sud    | 61 200  | 57 800  |

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(CASE WHEN mois = '2025-01' THEN revenu ELSE 0 END) AS "2025-01",
    SUM(CASE WHEN mois = '2025-02' THEN revenu ELSE 0 END) AS "2025-02"
FROM ventes
GROUP BY region
ORDER BY 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(CASE WHEN statut = 'ouvert'    THEN nb_tickets ELSE 0 END) AS ouvert,
    SUM(CASE WHEN statut = 'en_cours'  THEN nb_tickets ELSE 0 END) AS en_cours,
    SUM(CASE WHEN statut = 'resolu'    THEN nb_tickets ELSE 0 END) AS resolu,
    SUM(nb_tickets) AS total
FROM tickets
GROUP BY agent
ORDER BY 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
GROUP BY 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
ORDER BY 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")
)
ORDER BY 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 NOT EXISTS tablefunc;

-- Utilisation
SELECT *
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 mois
SELECT @colonnes = STRING_AGG(QUOTENAME(mois), ', ')
FROM (SELECT DISTINCT mois FROM ventes) AS mois_distincts;

-- Construire et exécuter la requête PIVOT
SET @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
CROSS JOIN LATERAL (
    VALUES ('2025-01', "2025-01"),
           ('2025-02', "2025-02")
) AS t(mois, revenu)
ORDER BY 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(CASE WHEN mois = '2025-01' THEN revenu END), 0) AS "2025-01",
    COALESCE(SUM(CASE WHEN mois = '2025-02' THEN revenu END), 0) AS "2025-02"
FROM ventes
GROUP BY 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)

Pour un traitement complet de l'optimisation, référez-vous à notre article sur les techniques d'optimisation des requêtes SQL.

Questions fréquentes

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.

Voir les exercices