Les candidats échouent souvent sur six concepts SQL clés en entretien data : fonctions fenêtres, filtrage avec agrégats, auto-jointures, CTE vs sous-requêtes, gestion des NULLs, et déduplication groupée. Comprendre et maîtriser ces notions accroît vos chances de réussite.
3 principaux points à retenir.
- Les fonctions fenêtres demandent un ordre explicite pour produire des résultats corrects.
- La clause HAVING filtre après agrégation, contrairement à WHERE.
- CTEs rendent les requêtes complexes plus lisibles et maintenables.
Pourquoi les fonctions fenêtres posent-elles problème ?
Les fonctions fenêtres dans SQL, c’est un peu comme la magie des chiffres, mais voilà, trop de candidats se retrouvent sur les planches et donnent du spectacle au lieu de sortir leurs véritables talents. Pourquoi ? Parce qu’ils oublient l’une des règles fondamentales : la clause ORDER BY. C’est vraiment LE point de friction qui fait souvent chuter les performances en entretien data. Sans ORDER BY, tes résultats deviennent totalement chaotiques, arbitraires, et tu perds la finesse qu’offre cette fonctionnalité.
Pense à des fonctions comme LAG() ou LEAD(), sans un bon ORDER BY, on se retrouve vite à jongler avec des informations qui n’ont ni queue ni tête. Imaginons un cas où tu cherches à trouver un achat effectué dans les 7 jours suivant un précédent. Si tu ne spécifies pas comment trier les données, tu obtiens des résultats qui ne signifient rien. Un peu comme si tu essayais de lire un livre sans en respecter l’ordre des chapitres !
Pour illustrer ça, voici une erreur classique et sa correction :
-- Mauvaise requête sans ORDER BY
SELECT
customer_id,
purchase_date,
LAG(purchase_date) OVER (PARTITION BY customer_id) AS previous_purchase
FROM
purchases;
-- Bonne requête avec ORDER BY
SELECT
customer_id,
purchase_date,
LAG(purchase_date) OVER (PARTITION BY customer_id ORDER BY purchase_date) AS previous_purchase
FROM
purchases;
Dans la seconde requête, en ajoutant ORDER BY purchase_date, tu garantis que LAG() renvoie la date d’achat correcte. Cela t’assure que, peu importe combien de données tu as, tu cuirais toujours correctement le timing entre les achats.
Éviter cette confusion renforce non seulement la fiabilité de tes résultats mais te permet également de briller dans un entretien. Tu démontres ainsi que tu comprends le cadre d’exécution et que tu sais comment manipuler les données avec précision. Et qui dit précision, dit confiance, et qui dit confiance, dit chances d’embauche !
Quelle est la différence entre WHERE et HAVING ?
La distinction entre WHERE et HAVING est souvent floue pour beaucoup, et cette confusion peut coûter cher lors d’un entretien. En effet, une seule requête peut susciter des erreurs embarrassantes. Alors, clarifions cela. WHERE s’applique avant la phase d’agrégation des données, tandis que HAVING agit après cette agrégation. Si vous essayez d’utiliser une fonction d’agrégat dans une clause WHERE, vous allez droit dans le mur !
Examinons le cycle d’exécution typique d’une requête SQL, qui se déroule comme suit :
- FROM
- WHERE
- GROUP BY
- HAVING
- SELECT
Pour illustrer l’erreur commune, prenons un exemple. Supposons que nous souhaitons récupérer le nom des équipes avec un score minimal de 10 points. Une requête mal formulée serait :
SELECT team_name, MIN(points)
FROM games
WHERE MIN(points) > 10
GROUP BY team_name;
Ici, l’usage de MIN(points) dans WHERE va provoquer une erreur. Pourquoi ? Parce que WHERE ne peut pas filtrer les résultats agrégés. En fait, le filtre doit être appliqué à l’étape HAVING. Voyons la requête correcte :
SELECT team_name, MIN(points)
FROM games
GROUP BY team_name
HAVING MIN(points) > 10;
Dans ce cas, nous regroupons d’abord les points par équipe et ensuite, nous appliquons le filtre pour ne garder que celles qui ont un score minimum supérieur à 10. C’est d’une simplicité redoutable, mais tant de candidats se plantent sur cette subtilité.
Pour renforcer cette compréhension, il est utile de se rappeler que les fonctions d’agrégat sont surtout conçues pour être utilisées avec HAVING, car cette clause s’exécute après que le groupe a été formé. En maîtrisant cette distinction, vous serez non seulement plus compétent en SQL, mais également mieux préparé pour impressionner vos recruteurs. Pour une lecture complémentaire sur la différence entre WHERE et HAVING, cliquez ici.
Comment utiliser les auto-jointures pour comparer des événements ?
Les auto-jointures, ou self-joins, sont souvent sous-estimées par les candidats lors des entretiens pour des postes liés aux données. Pourtant, ces requêtes qui consistent à joindre une table à elle-même peuvent s’avérer être des alliées redoutables, notamment pour comparer des événements dans le temps. Pourquoi en parler ? Parce qu’elles sont souvent mieux adaptées que des sous-requêtes imbriquées, tant en lisibilité qu’en performance.
Imaginons par exemple que nous travaillons avec une table des taux de change. Si nous souhaitons comparer les taux à deux dates différentes, une auto-jointure pourrait sembler la solution évidente. Prenons une table nommée exchange_rates avec les colonnes currency, rate et date.
Voici comment une sous-requête complexe pourrait être formulée pour le même objectif :
SELECT
e1.currency,
e1.rate AS rate_on_date1,
(SELECT rate
FROM exchange_rates e2
WHERE e2.currency = e1.currency
AND e2.date = '2023-01-01') AS rate_on_date2
FROM exchange_rates e1
WHERE e1.date = '2023-01-02';
Ce code, bien que valide, présente une complexité qui pourrait faire perdre du temps inutilement. Maintenant, passons à notre auto-jointure :
SELECT
e1.currency,
e1.rate AS rate_on_date1,
e2.rate AS rate_on_date2
FROM exchange_rates e1
JOIN exchange_rates e2 ON e1.currency = e2.currency
WHERE e1.date = '2023-01-02'
AND e2.date = '2023-01-01';
En utilisant l’auto-jointure, on simplifie notre requête tout en améliorant la lisibilité. Chaque élément est directement issu de la même table, évitant les fouillis d’imbrications qui rendent la maintenance du code plus chaotique.
En plus de la clarté, une auto-jointure est souvent plus performante. Le moteur de base de données peut optimiser les jointures de manière plus efficace que les imbroglios de sous-requêtes. C’est un faux ami que de penser que les sous-requêtes sont toujours plus simples. Alors, la prochaine fois que vous devrez analyser une évolution dans le temps ou toute autre comparaison d’enregistrements au sein d’une même table, pensez à l’auto-jointure comme à votre fidèle compagne.
Pour une plongée plus profonde sur le sujet, la lecture de cet article pourrait vous éclairer encore plus sur les relations hiérarchiques en SQL.
Pourquoi privilégier les CTEs aux sous-requêtes imbriquées ?
Quand il s’agit de requêtes SQL complexes, la question qui revient souvent est : faut-il utiliser des Common Table Expressions (CTEs) ou se perdre dans les méandres des sous-requêtes imbriquées? C’est un peu comme choisir entre un bon verre de vin et un cocktail explosif. Le vin, c’est beau, c’est clair, ça coule tout seul, tandis que le cocktail…le cocktail peut vous exploser à la figure si vous n’y prêtez pas attention !
Les CTEs, grâce à la syntaxe WITH, permettent de rendre les requêtes lisibles comme un bon roman, tandis que les sous-requêtes imbriquées peuvent rapidement devenir un véritable cauchemar de maintenabilité. Imaginez vos collègues essayant de décortiquer une sous-requête de plusieurs niveaux… Ça devient vite une séance de désencombrement de code. La clarté d’une CTE vous permet de réutiliser des requêtes sans la redondance qui alourdit les sous-requêtes imbriquées. En fait, une bonne CTE devient presque poétique.
Pour illustrer cela, prenons un cas concret : nous souhaitons calculer les top acteurs par genre à partir de données regroupées. Voici comment cela se présente en CTE :
WITH GenreStats AS (
SELECT
genre,
acteur,
COUNT(*) AS nombre_films
FROM films
GROUP BY genre, acteur
)
SELECT
genre,
acteur,
nombre_films
FROM GenreStats
ORDER BY genre, nombre_films DESC;
Voici la même logique avec une sous-requête imbriquée :
SELECT
genre,
acteur,
COUNT(*) AS nombre_films
FROM (
SELECT
genre,
acteur
FROM films
) AS SousRequete
GROUP BY genre, acteur
ORDER BY genre, nombre_films DESC;
Dans cet exemple, la version avec CTE est non seulement plus lisible, mais elle permet aussi d’éviter la duplication de logiques, ce qui est un point essentiel dans la maintenabilité du code SQL.
La leçon ici est simple : lorsque vous rencontrez une requête complexe, n’oubliez pas que la clarté et la maintenabilité sont primordiales. Optez pour les CTEs, elles vous feront gagner un temps précieux, tant lors de la création que lors de la relecture. En somme, choisissez la clarté d’esprit plutôt que le tumulte des sous-requêtes
Comment gérer correctement les NULLs dans SQL ?
Dans le monde SQL, le NULL est un petit mais redoutable monstre. Qui aurait cru qu’un simple mot pouvait causer tant de tracas ? La première leçon à apprendre, c’est que NULL n’est égal à rien, même pas à NULL. Oui, vous avez bien entendu. Si un candidat répond à une question en utilisant = NULL pour tester des valeurs NULL, la seule chose qu’il peut récolter, c’est un regard désabusé de l’intervieweur. Pourquoi ? Parce qu’en SQL, un test de valeur NULL doit être effectué avec IS NULL ou IS NOT NULL pour vérifier si une valeur est absente.
Imaginez que vous ayez deux tables qui contiennent des informations sur des clients et des commandes. Si vous réalisez un FULL OUTER JOIN entre ces deux tables, vous risquez d’être envahi par des NULLs, surtout si certains clients n’ont pas passé de commandes. Prenons un exemple concret :
SELECT c.client_id, COUNT(o.order_id) AS order_count
FROM clients c
FULL OUTER JOIN orders o ON c.client_id = o.client_id
GROUP BY c.client_id;
Dans ce cas, lorsque certains clients n’ont pas de commandes associées, order_count renverra des NULLs, ce qui n’est pas ce que nous voulons. Que faire alors ? C’est là que COALESCE() entre en jeu. Cette fonction permet de prendre le premier argument non NULL d’une liste donnée. Voici comment vous pourriez utiliser COALESCE pour gérer ça :
SELECT c.client_id, COALESCE(COUNT(o.order_id), 0) AS order_count
FROM clients c
FULL OUTER JOIN orders o ON c.client_id = o.client_id
GROUP BY c.client_id;
En utilisant COALESCE ici, si l’utilisateur n’a pas passé de commande, nous le remplaçons par 0 au lieu d’un NULL. Cela nous permet de garder constamment des compteurs et identifiants propres et compréhensibles. La gestion des NULLs est cruciale, et ne pas le faire correctement peut mener à des rapports erronés ou à des erreurs dans les analyses de données. Si vous souhaitez approfondir ce sujet, je vous recommande de jeter un coup d’œil à cet article sur la gestion des NULLs.
Comment dédupliquer des groupes en sélectionnant la meilleure ligne ?
Dédupliquer des groupes en sélectionnant la meilleure ligne selon un critère peut sembler être un casse-tête pour beaucoup de candidats lors des entretiens data. Mettons les choses au clair : la solution de facilité, qui consiste à utiliser GROUP BY associé à LIMIT, n’est pas la panacée. En fait, elle échoue souvent là où on l’attendrait le moins.
Imaginons que vous devez trouver le meilleur produit vendu chaque mois, selon les ventes totales. La méthode GROUP BY avec LIMIT 1 pourrait sembler suffisante sur le papier, mais en réalité, elle peut vous jouer des tours. Si vous avez plusieurs produits vendus avec la même somme de ventes pour un mois donné, cette méthode ne saura pas choisir efficacement entre eux. Le résultat pourrait être biaisé, omettant certains produits ayant atteint le même sommet de ventes.
Voici un exemple concret. Supposons que nous avons une table ventes contenant les colonnes produit, mois, et total_ventes :
produit | mois | total_ventes
---------- | --------- | -------------
Produit A | 2023-01 | 500
Produit B | 2023-01 | 500
Produit C | 2023-01 | 300
Produit D | 2023-02 | 600
Produit E | 2023-02 | 450
En utilisant GROUP BY et LIMIT, vous obtiendrez un seul résultat par mois, sans garantie que vous attrapiez le meilleur des meilleurs. Pour éviter cela, nous passons à la vitesse supérieure avec les fonctions de classement.
Utiliser RANK() dans une sous-requête est le bon chemin à suivre. Voici comment procéder :
SELECT produit, mois, total_ventes
FROM (
SELECT produit, mois, total_ventes,
RANK() OVER (PARTITION BY mois ORDER BY total_ventes DESC) AS rnk
FROM ventes
) AS sous_requete
WHERE rnk = 1;
Cette requête attribue un rang à chaque produit par mois. En spécifiant WHERE rnk = 1, vous obtenez tous les produits qui partagent le même total de ventes le plus élevé, gérant ainsi les égalités de manière élégante.
En somme, recourir à RANK() vous permet de façonner vos résultats sans compromettre l’intégrité des données. Cela pourrait ne pas être la première solution à laquelle pensent les personnes que vous interrogez, mais une fois maîtrisée, cette technique est un atout indéniable dans votre arsenal SQL.
Comment ces méthodes SQL vont-elles booster vos performances en entretien ?
Maîtriser ces six concepts SQL – fonctions fenêtres bien ordonnées, bon usage de HAVING, auto-jointures, CTEs pour la clarté, gestion intelligente des NULLs et déduplication avec fonctions de classement – est indispensable pour réussir vos entretiens data. Ces notions sont des pièges classiques où beaucoup trébuchent, mais elles restent incontournables à comprendre et pratiquer. En les intégrant, vous gagnerez en efficacité, fiabilité et maintenabilité dans vos requêtes. Ce sont ces compétences qui feront la différence face aux recruteurs exigeants et vous ouvriront les portes des postes data stratégiques.
FAQ
Pourquoi les fonctions fenêtres nécessitent-elles un ORDER BY dans SQL ?
Quelle est la différence entre WHERE et HAVING en SQL ?
Quand faut-il utiliser une auto-jointure en SQL ?
Pourquoi privilégier les CTEs au lieu des sous-requêtes imbriquées ?
Comment gérer les NULLs pour éviter les erreurs en SQL ?
A propos de l’auteur
Franck Scandolera, fort d’une longue expérience de consultant et formateur en analytics et Data Engineering, accompagne professionnels et entreprises dans la maîtrise avancée de SQL et des infrastructures data. Responsable de l’agence webAnalyste, expert en automatisation et IA, il vulgarise et transmet des compétences techniques pointues pour transformer les données en leviers d’action concrets.
⭐ Analytics engineer, Data Analyst et Automatisation IA indépendant ⭐
- Ref clients : Logis Hôtel, Yelloh Village, BazarChic, Fédération Football Français, Texdecor…
Mon terrain de jeu :
- Data Analyst & Analytics engineering : tracking avancé (GTM server, e-commerce, CAPI, RGPD), entrepôt de données (BigQuery, Snowflake, PostgreSQL, ClickHouse), modèles (Airflow, dbt, Dataform), dashboards décisionnels (Looker, Power BI, Metabase, SQL, Python).
- Automatisation IA des taches Data, Marketing, RH, compta etc : conception de workflows intelligents robustes (n8n, App Script, scraping) connectés aux API de vos outils et LLM (OpenAI, Mistral, Claude…).
- Engineering IA pour créer des applications et agent IA sur mesure : intégration de LLM (OpenAI, Mistral…), RAG, assistants métier, génération de documents complexes, APIs, backends Node.js/Python.






