Quels patterns analytiques un data scientist doit maîtriser?

Maîtriser les patterns analytiques (joins, window functions, agrégations, pivot) permet d’extraire des insights fiables et réutilisables ; PostgreSQL illustre ces techniques via ses fonctions fenêtrées et ses agrégats (PostgreSQL docs). Je détaille motifs, SQL types et cas d’usage pour les appliquer tout de suite.

Comment trouver le bon sous-ensemble avec joins et filtres

On identifie d’abord la table primaire, puis on joint les tables secondaires et on applique des filtres pour isoler le sous-ensemble pertinent.

La logique du motif «Joins + Filters» repose sur quatre étapes claires.

  • Identification de la table primaire : Choisir l’entité centrale qui détermine le contexte (ici flight_schedule pour un vol donné).
  • Choix du type de jointure : Préférer INNER JOIN quand on ne veut que les correspondances, LEFT JOIN quand on doit conserver la ligne primaire même sans correspondance.
  • Conditions de jointure : Écrire des conditions précises dans ON pour éviter les produits cartésiens et pour limiter le volume dès la jointure.
  • Filtres WHERE/HAVING : Appliquer des filtres après la jointure pour restreindre le résultat final, ou utiliser HAVING pour les agrégations.

Bonnes pratiques importantes : Sélectionner uniquement les colonnes nécessaires pour réduire le trafic réseau et la mémoire, indexer les colonnes utilisées en WHERE et ON, et filtrer tôt (pré-filtrage via CTE ou sous-requêtes) pour diminuer les données jointes.

Pièges courants : Un ON manquant ou incorrect peut créer un produit cartésien massif, et placer un filtre sur la mauvaise table (par exemple filtrer après un LEFT JOIN comme si c’était INNER) change le résultat.

WITH f AS (
  SELECT flight_id, duration
  FROM flight_schedule
  WHERE flight_id = 101
)
SELECT e.movie_id, e.title, e.duration
FROM f
JOIN entertainment_catalog e
  ON e.duration 

WITH f AS (...) Préfiltre le vol ciblé pour réduire la table primaire à une ligne.

SELECT e.movie_id, e.title, e.duration Choisit uniquement les colonnes utiles pour l'usage analytique.

FROM f JOIN entertainment_catalog e ON e.duration

ORDER BY e.duration DESC Classe les films du plus long au plus court pour la sélection.

Pour de gros volumes, indexer flight_schedule(flight_id) et entertainment_catalog(duration) aide, limiter les colonnes renvoyées réduit I/O, utiliser JOIN LATERAL pour des critères dépendant ligne à ligne, et préfiltrer les deux tables via CTE si possible.

  • RH : Associer employés et formations pour n'afficher que les formations éligibles selon ancienneté.
  • Retail : Joindre transactions et catalogue pour ne garder que les promos applicables au panier.
  • Streaming : Filtrer catalogue selon la durée et le profil utilisateur pour proposer only contenus adaptés.
Pattern Étapes Snippet SQL Cas d'usage
Joins + Filters Identifier table primaire → Choisir JOIN → Écrire ON → Filtrer WHERE/HAVING WITH f AS (... ) SELECT ... FROM f JOIN e ON e.duration Streaming / Retail / RH

Quand utiliser les fonctions fenêtrées pour classer

Les fonctions fenêtrées servent à classer ou ordonner les enregistrements tout en conservant le contexte des partitions.

Principe : Une window function calcule des valeurs sur un ensemble de lignes appelées « partition » tout en conservant chaque ligne d'origine.

ORDER BY dans OVER() détermine l'ordre interne à la partition et influence le résultat de RANK(), DENSE_RANK() et ROW_NUMBER().

RANK() attribue le même rang aux ties puis saute des positions, DENSE_RANK() attribue le même rang mais sans sauts, ROW_NUMBER() donne un numéro unique (utile pour déduplication).

SELECT channel_id, post_id, likes, RANK() OVER (PARTITION BY channel_id ORDER BY likes DESC) AS rnk FROM posts;

Pour ne garder que les 3 publications les plus likées par canal utiliser une sous-requête ou CTE :

SELECT channel_id, post_id, likes
FROM (
  SELECT channel_id, post_id, likes,
         RANK() OVER (PARTITION BY channel_id ORDER BY likes DESC) AS rnk
  FROM posts
) t
WHERE rnk 

En cas d'égalité, RANK() retournera plusieurs lignes avec le même rang et sautera des positions (ex. 1,1,3), DENSE_RANK() donnera 1,1,2 et ROW_NUMBER() forcera un ordre arbitraire (1,2,3).

Performances : Les window functions lisent toutes les lignes concernées par la partition après les filtres. Utiliser des index couvrants sur (channel_id, likes DESC) pour accélérer le tri. Pré-filtrer avec WHERE ou CTE pour réduire le volume, et envisager le partitionnement physique si la table est massive.

  • Vente : Top vendeurs par région pour fixer des quotas mensuels.
  • Éducation : Top étudiants par promotion pour bourses et distinctions.
  • Logistique : Top livreurs par zone pour optimiser incentives.

Exemple de déduplication avec ROW_NUMBER :

WITH ranked AS (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY external_id ORDER BY updated_at DESC) AS rn
  FROM items
)
DELETE FROM items
USING ranked
WHERE items.id = ranked.id AND ranked.rn > 1;
Fonction Comportement sur ties Exemple SQL Cas d'usage
RANK() Même rang, sauts RANK() OVER (PARTITION BY ch ORDER BY likes DESC) Top N en incluant ex‑æquo
DENSE_RANK() Même rang, pas de sauts DENSE_RANK() OVER (...) Classement compact
ROW_NUMBER() Unique, aucun tie ROW_NUMBER() OVER (...) Déduplication, choix d'un enregistrement

Comment agréger et faire du roll-up pour résumés

L'agrégation et le grouping (ROLLUP/GROUPING SETS) permettent de résumer les données par dimensions analytiques et d'obtenir des totaux hiérarchiques.

Le motif Aggregation + Grouping consiste à choisir des dimensions pertinentes, appliquer un GROUP BY, calculer des métriques avec COUNT/SUM/AVG et filtrer les groupes avec HAVING si nécessaire.

Choisir les dimensions : Sélectionner les axes analytiques (par ex. user_id, session_date).

  • Appliquer GROUP BY : Regrouper les lignes pour résumer.
  • Utiliser COUNT/SUM/AVG : Calculer nombre d'événements, montants, moyennes.
  • Filtrer avec HAVING : Exclure groupes non pertinents (ex. SUM(total)=0).

Exemple pratique : Trouver les utilisateurs ayant commencé une session et passé une commande le même jour en joignant sessions (distinct user_id, session_date) et orders, puis GROUP BY user_id, session_date pour COUNT et SUM.

WITH sessions_day AS (
  -- Une session par utilisateur et par jour
  SELECT DISTINCT user_id, session_date::date AS session_date
  FROM sessions
),
orders_day AS (
  -- Total des commandes par utilisateur et par jour
  SELECT user_id, order_date::date AS session_date, SUM(total) AS total_order
  FROM orders
  GROUP BY user_id, session_date
)
SELECT
  s.user_id,
  s.session_date,
  COUNT(o.*) AS orders_count,            -- nombre de lignes de orders liées
  COALESCE(o.total_order, 0) AS total_order  -- montant total des commandes ce jour
FROM sessions_day s
LEFT JOIN orders_day o
  ON s.user_id = o.user_id
  AND s.session_date = o.session_date
GROUP BY s.user_id, s.session_date;

Le pattern Roll-Up permet d'obtenir plusieurs niveaux d'agrégation en une requête : par jour, par utilisateur, et un total général.

Exemple :

SELECT
  user_id,
  session_date,
  COUNT(*) AS sessions_count,
  SUM(total_order) AS total_order
FROM combined_table
GROUP BY ROLLUP (user_id, session_date);

On peut aussi expliciter avec GROUPING SETS pour contrôler les combinaisons (par ex. (user_id, session_date), (user_id), ()).

Cas d'usage :

  • E-commerce : Résumer ventes par produit, par jour, par catégorie pour détecter tendances.
  • SaaS : Mesurer activation par utilisateur et par période pour suivre churn.
  • Finance : Consolider positions par compte, par jour et totaux pour reporting réglementaire.

Limites : Cardinalité élevée des dimensions peut exploser le nombre de groupes et impacter les performances ; Attention aux scans complets et besoin possible d'index, de pré-agrégations ou de data-warehouse.

Opération SQL type Résultat attendu Performance tips
Agrégation simple GROUP BY Métriques par dimension Index sur colonnes GROUP BY, partitions
Totaux hiérarchiques ROLLUP / GROUPING SETS Niveaux agrégés (détail → total) Limiter cardinalité, pré-agréger
Filtrage groupes HAVING Groupes pertinents seulement Appliquer WHERE avant GROUP BY

Comment transformer des lignes en colonnes avec pivot

Le pivot transforme des lignes agrégées en colonnes pour des rapports lisibles ; on peut utiliser FILTER avec des agrégats ou la fonction crosstab de PostgreSQL.

Objectif du motif «Pivoting». Rendre lisible un tableau où une dimension catégorielle (type de paiement, canal, mois) doit devenir des colonnes pour comparaisons rapides et export BI.

Méthodes courantes et explications succinctes avant la liste des avantages/inconvénients.

  • SUM(...) FILTER (WHERE ...): Agrége directement par condition, syntaxe claire et performante, idéale quand les catégories sont connues.
  • SUM(CASE WHEN ... THEN ... ELSE 0 END): Compatible avec presque tous les SGBD, utile si FILTER n'est pas disponible.
  • tablefunc.crosstab: Produit un pivot «véritable» moins verbeux en SQL mais nécessite l'extension et des colonnes fixes dans la signature de sortie.

Exemple lié aux paiements les plus élevés par entité (ville San Francisco). Exemple avec FILTER pour total par type de paiement par ville :

SELECT city,
  SUM(amount) FILTER (WHERE payment_type = 'card') AS card_total,
  SUM(amount) FILTER (WHERE payment_type = 'cash') AS cash_total
FROM payments
GROUP BY city;

Implémentation crosstab (activer l'extension tablefunc) :

CREATE EXTENSION IF NOT EXISTS tablefunc;
SELECT *
FROM crosstab(
  $$SELECT city, payment_type, SUM(amount)
    FROM payments
    GROUP BY city, payment_type
    ORDER BY city, payment_type$$
) AS ct(city text, card_total numeric, cash_total numeric, check_total numeric);

Génération dynamique des colonnes. Requérir une étape supplémentaire pour interroger DISTINCT payment_type et construire la requête SQL dynamiquement via PL/pgSQL ou l'outil applicatif, ce qui augmente la complexité mais permet d'éviter les changements manuels.

Impacts sur tableaux de bord et maintenance. Les pivots SQL centralisent la logique et diminuent la charge côté BI, mais rendent les modifications de schéma plus coûteuses à maintenir. Les outils BI peuvent pivoter dynamiquement et gérer les colonnes variables sans déploiement SQL.

Cas d'usage rapides :

  • Reporting périodique pour export CSV/Excel.
  • Dashboards opérationnels nécessitant colonnes stables et performances rapides.
  • Export pour ETL/BI quand la source doit être normalisée avant ingestion.
Méthode Requis Avantages Inconvénients
SUM(...) FILTER PostgreSQL 9.4+ Clair, performant, pas d'extension Colonnes fixes, maintenance si catégories changent
CASE WHEN SUM Any SQL Portable, simple Verbeux, moins lisible
crosstab (tablefunc) Extension tablefunc, colonnes connues Sortie compacte, bonne pour beaucoup de catégories Nécessite signature fixe et gestion dynamique complexe

Prêt à appliquer ces patterns pour produire des analyses exploitables ?

Les patterns analytiques — joins+filters, window functions, aggregation/roll-up et pivot — forment une boîte à outils réutilisable pour tout data scientist travaillant en SQL. Maîtriser chaque motif vous fait gagner en rapidité, robustesse et scalabilité : requêtes plus performantes, résultats fiables et rapports exploitables. En appliquant ces templates SQL et bonnes pratiques, vous transformez des données brutes en décisions actionnables, immédiates et mesurables.

FAQ

Quels sont les quatre patterns analytiques essentiels à maîtriser ?
Les quatre patterns clés sont : joins + filters (isoler sous-ensembles), window functions (classements et contextes), aggregation + grouping (résumés et roll-up) et pivoting (lignes→colonnes pour reporting).
Quand utiliser RANK() plutôt que ROW_NUMBER() ?
RANK() conserve des égalités (ties) en donnant le même rang aux ex-aequo, créant des sauts dans la numérotation ; ROW_NUMBER() force un rang unique. Choisissez RANK() pour top-N où les ties comptent, ROW_NUMBER() pour déduplication.
Quelle méthode de pivot est préférable en PostgreSQL ?
Pour un nombre fixe de catégories, utilisez SUM(...) FILTER (WHERE ...) ou CASE WHEN. Pour des pivot dynamiques, crosstab(tablefunc) est performant mais demande une préparation des données et l'extension tablefunc.
Comment améliorer les performances des requêtes analytiques lourdes ?
Limiter les colonnes sélectionnées, ajouter des indexes pertinents, pré-agréger via CTE ou tables matérialisées, éviter les JOINs cartésiens, et profiler les plans d'exécution (EXPLAIN ANALYZE) pour identifier les goulots.
Comment pratiquer ces patterns en vue d'un entretien technique ?
Travaillez des exercices concrets : écrire requêtes joins+filters, top-N with windows, rollups avec GROUPING SETS, et pivot via FILTER/crosstab. Versionnez vos solutions, commentez-les et mesurez les temps d'exécution sur jeux de données variés.

 

 

A propos de l'auteur

Franck Scandolera — expert & formateur en Tracking avancé server-side, Analytics Engineering, automatisation No/Low Code (n8n) et intégration de l'IA en entreprise. Responsable de l'agence webAnalyste et de l'organisme Formations Analytics. Références : Logis Hôtel, Yelloh Village, BazarChic, Fédération Française de Football, Texdecor. Dispo pour aider les entreprises => contactez moi.

Retour en haut
AIgenierie