Les candidats échouent souvent sur six concepts SQL clés en entretien data : fonctions fenêtres, agrégats, auto-jointures, CTE vs sous-requêtes, gestion des NULLs et déduplication. Maîtriser ces notions est crucial pour réussir vos entretiens et éviter des erreurs fréquentes.
3 principaux points à retenir.
- Les fonctions fenêtres demandent une vraie compréhension du frame, partition et ordre.
- HAVING filtre après agrégation, WHERE avant ; confondre génère des erreurs.
- La gestion des NULL et la déduplication nécessitent des techniques spécifiques.
Pourquoi échouent-ils sur les fonctions fenêtres
Les fonctions fenêtres, qu’est-ce que c’est ? En gros, ce sont des outils puissants en SQL qui permettent d’effectuer des calculs sur un ensemble de lignes, tout en gardant en tête leur relation avec d’autres lignes. Mais, devinez quoi ? Beaucoup de candidats échouent lors des entretiens à cause d’un concept crucial : la clause ORDER BY dans les fonctions telles que LAG(), LEAD() ou RANK(). Pourquoi est-ce si important ? Parce que sans elle, vous risquez de totalement dérailler vos résultats.
Imaginez une situation : vous souhaitez analyser les achats des clients pour détecter s’ils effectuent un second achat dans un délai donné. Supposons que vous ayez une table achats contenant les colonnes client_id, date_achat et montant. Si vous vous essayez à faire une requête sans ORDER BY, vous pourriez facilement vous retrouver avec des résultats déroutants.
SELECT client_id,
date_achat,
LAG(date_achat) OVER (PARTITION BY client_id) AS dernier_achat
FROM achats;
Avec cette requête, ce qui se passe ? La fonction LAG() va récupérer la date du dernier achat, mais sans ORDER BY, ça pourrait être n’importe quelle date d’achat, pas forcément la plus récente. Magique, non ? Surtout quand on veut savoir si un client a acheté deux fois en moins de 30 jours ! Alors, comment y remédier ? Ajoutez l’ordre :
SELECT client_id,
date_achat,
LAG(date_achat) OVER (PARTITION BY client_id ORDER BY date_achat) AS dernier_achat
FROM achats;
Avec cette correction, la fonction LAG() se comportera comme il se doit, en analysant les achats du plus ancien au plus récent. Ça devient déjà beaucoup plus clair, non ? En utilisant ORDER BY, on s’assure que le cadre est bien défini, permettant ainsi une analyse précise.
À noter que la compatibilité de ces fonctions varie selon les dialectes SQL. Par exemple, SQL Server, PostgreSQL ou même MySQL peuvent avoir des spécificités qui influencent le comportement des fonctions fenêtres. Alors, ne vous fiez pas seulement à votre instinct et vérifiez les docs pour éviter les mauvaises surprises lors de votre prochaine interview ou projet. Pour un aperçu sur ce sujet, vous pouvez lire cet échange intéressant sur Reddit !
Quelle différence entre WHERE et HAVING en SQL
Quand on parle de SQL, il y a un point qui revient souvent comme un mantra au bord de l’échec : quelle différence entre WHERE et HAVING ? Si vous froncez les sourcils à cette question, respirez. On va déballer ça avec un exemple bien ancré dans la réalité des données.
Commençons par l’ordre d’exécution des clauses SQL. La logique, c’est la suivante :
- FROM : Choisissez vos tables.
- JOIN : Associez-les si besoin.
- WHERE : Filtrez les lignes selon des conditions spécifiques.
- GROUP BY : Regroupez les résultats.
- HAVING : Filtrez les groupes selon des conditions d’agrégation.
On voit ici que WHERE opère avant l’agrégation alors que HAVING n’intervient qu’après. C’est crucial lorsque l’on veut filter des données agrégées. Une confusion typique est d’essayer d’ajouter une condition d’agrégation dans la clause WHERE. Imaginez, par exemple, que nous avons un dataset de vins et que vous voulez trouver tous les cépages dont la somme des ventes est supérieure à 1000bouteilles.
SELECT cepage, SUM(ventes) AS total_ventes
FROM vins
WHERE SUM(ventes) > 1000 -- CECI GÉNÈRE UNE ERREUR
GROUP BY cepage;
Cette requête va planter comme un bon vieux vin de 1990 ; SQL n’aime pas voir des agrégats dans WHERE. La bonne méthode est de passer par HAVING :
SELECT cepage, SUM(ventes) AS total_ventes
FROM vins
GROUP BY cepage
HAVING SUM(ventes) > 1000; -- CECI EST CORRECT
La compréhension de cette distinction est souvent mal intégrée, même par des pros. Les programmes de formation, malgré leurs bonnes intentions, laissent parfois ces concepts subtils de côté. Dans un entretien, si vous ne démontrez pas cette clarté, attendez-vous à avoir un regard perplexe de l’intervieweur. Ne pas maîtriser la différence entre WHERE et HAVING peut vous coûter la place, car cela révèle un manque de rigueur, voire d’attention aux détails, très prisés dans le domaine des données.
Pour creuser la question, vous pouvez consulter cet article intéressant qui aborde en profondeur cette distinction : Quelle est la différence entre WHERE et HAVING ?
Comment simplifier les comparaisons temporelles par auto-jointure
Dans le monde des bases de données, la manipulation temporelle n’est pas qu’une simple question de dates. Elle demande réflexion, précision et parfois, un peu de flair. Une technique souvent sous-estimée pour traiter des comparaisons temporelles est l’auto-jointure. Plutôt que de se perdre dans des sous-requêtes imbriquées et une complexité souvent pénible, l’auto-jointure permet de comparer des valeurs dans une seule table de manière élégante et efficace.
Parlons des taux de change, par exemple. Supposons que l’on souhaite comparer le taux de change d’une devise à deux dates clés. Avec une auto-jointure, cela devient un jeu d’enfant. Pourquoi ? Car cette méthode vous permet de juxtaposer les données de la même table sans avoir à faire des gymnastiques inutiles. Voici un exemple de SQL pour illustrer cela :
SELECT a.date AS date1,
a.taux AS taux_date1,
b.date AS date2,
b.taux AS taux_date2
FROM taux_de_change a
JOIN taux_de_change b
ON a.devise = b.devise
WHERE a.date = '2023-01-01'
AND b.date = '2023-12-31';
Dans ce code, nous joignons la table taux_de_change avec elle-même, en utilisant des alias (ici a et b) pour dénoter des enregistrements différents à deux dates spécifiques. Ce qui se traduit par une requête plus lisible et plus maintenable. Avec des sous-requêtes, la complexité explose et la lisibilité s’en ressent.
Pour bien visualiser la différence entre ces deux méthodes, voici un tableau synthèse :
| Aspect | Sous-requête | Auto-jointure |
|---|---|---|
| Lisibilité | Difficile à suivre | Clair et concis |
| Performance | Plus lente | Plus rapide |
| Complexité | Élevée | Faible |
| Maitrise | Risque d’erreurs | Plus maîtrisable |
À la fin de la journée, choisir entre auto-jointure et sous-requête c’est un peu comme choisir entre une mélodie claire et harmonieuse et un bruit de fond chaotique. Vous voulez la première, n’est-ce pas ? Pour aller plus loin sur ce sujet, vous pouvez consulter cet article intéressant sur les auto-jointures dans SQL.
Pourquoi préférer les CTE aux sous-requêtes imbriquées
Les requêtes SQL, c’est un peu comme un puzzle. Quand on aborde la complexité d’un problème, il y a plusieurs façons de s’y prendre. Deux méthodes qui se battent dans la cour des grands sont les Common Table Expressions (CTE) et les sous-requêtes imbriquées. Mais alors, pourquoi préférer les CTE aux sous-requêtes ? La réponse réside dans la lisibilité et la maintenabilité du code.
Imaginons un scénario où nous souhaitons analyser les films et leurs genres. Utiliser des sous-requêtes imbriquées mène souvent à une structure de requête illisible. Prenons un exemple : si nous voulons sélectionner les acteurs d’un certain genre de film, les sous-requêtes vont s’enchasser les unes dans les autres comme une poupée russe. Voici une version simplifiée :
SELECT actor.name
FROM movies
JOIN (SELECT genre_id FROM genres WHERE name = 'Action') AS genre_filter
ON movies.genre_id = genre_filter.genre_id
JOIN actors ON movies.actor_id = actors.id;
Ce code devient vite illisible au fur et à mesure que les couches se superposent. En revanche, avec un CTE, nous pouvons déclarer clairement nos étapes avant de retrouver le même résultat :
WITH GenreMovies AS (
SELECT * FROM movies WHERE genre_id IN (SELECT id FROM genres WHERE name = 'Action')
)
SELECT actor.name
FROM GenreMovies
JOIN actors ON GenreMovies.actor_id = actors.id;
Avec les CTE, chaque section du code est ainsi nommée et peut être comprise en un clin d’œil. Cela rend bien plus facile d’analyser, déboguer, et surtout de maintenir les requêtes à long terme.
Pour illustrer les différences, voici un tableau comparatif :
| Aspect | Sous-requêtes imbriquées | CTE |
|---|---|---|
| Lisibilité | Peu lisible, alourdissant la requête | Clair, segmenté en étapes compréhensibles |
| Maintenabilité | Difficile, nécessite des ajustements redondants | Facile, les modifications se font à un seul endroit |
| Performance | Parfois inefficace avec des requêtes lourdes | Peut être optimisée par le SGBD |
Pour exceller en entretien, privilégiez l’utilisation des CTE, surtout lorsque vous devez naviguer dans des requêtes complexes. C’est une bonne pratique qui impressionnera vos examinateurs et les aidera à comprendre votre approche.
Quelle bonne pratique pour gérer les NULL en SQL
Quand on parle de NULL en SQL, il faut vraiment faire attention. Les novices, et même certains data analysts chevronnés, se laissent encore piéger par cette petite entité glissante. Confondre NULL avec une valeur, c’est comme confondre le vide avec le plein. Ça casse la logique et ça peut produire des résultats erronés. Vous avez déjà vu ça en entretien ? Le candidat qui utilise ‘=’ pour comparer une colonne à NULL, c’est un peu comme s’il se tirait une balle dans le pied, non ?
Pour illustrer cette problématique, imaginez un scénario avec des interactions entre utilisateurs et leurs messages. Supposons qu’on ait deux tables : Utilisateurs et Messages. Voyager dans l’univers des jointures est toujours délicat, mais avec une FULL OUTER JOIN, on espère obtenir toutes les données, même celles qui sont manquantes.
Voici comment peut se présenter notre requête, sans prendre en compte les NULL :
SELECT U.ID, COUNT(M.ID) AS Interactions
FROM Utilisateurs U
FULL OUTER JOIN Messages M ON U.ID = M.UserID
GROUP BY U.ID;
Si deux utilisateurs n’ont pas envoyé de message, le résultat de COUNT(M.ID) renverra NULL. Et là, le bas blesse ! En tentant de calculer des statistiques, toutes ces valeurs NULL vont fausser notre tableau final en créant des lacunes.
Au lieu d’une belle liste de statistiques claires, on se retrouve avec des valeurs qui ne signifient rien. C’est là qu’intervient le super-héros SQL : COALESCE. Cette fonction permet de remplacer les NULL par une valeur par défaut, et ça change la donne.
On corrige donc notre requête comme suit :
SELECT U.ID, COALESCE(COUNT(M.ID), 0) AS Interactions
FROM Utilisateurs U
FULL OUTER JOIN Messages M ON U.ID = M.UserID
GROUP BY U.ID;
En ajoutant COALESCE, on s’assure que si l’utilisateur n’a pas d’interactions, il sera affiché avec 0 au lieu de NULL. Une petite nuance qui, lors d’un entretien, montrera que vous savez comment gérer les subtilités du SQL. Vous ne voulez pas que l’on doute de votre capacité à gérer les données, n’est-ce pas ? Alors, mémorisez bien cette astuce, elle pourrait vous sauver la mise !
Comment faire une déduplication efficace par groupe
Dans le monde des bases de données, à chaque fois qu’on vous demande de ressortir une ligne unique par groupe, par exemple, le meilleur vendeur par mois, vous vous retrouvez face à un dilemme. Comment choisit-on cette fameuse ligne sans perdre des informations précieuses sur le reste de la ligne? Une première réaction pourrait être d’utiliser la méthode naïve GROUP BY avec LIMIT 1, mais accrochez-vous ! Cette approche peut vous mener droit dans le mur.
Le problème avec cette méthode est qu’elle ne garantit pas la récupération des bonnes informations. Imaginez : si vous avez plusieurs vendeurs avec le même chiffre d’affaires, qui allez-vous choisir ? La méthode ne vous donnera qu’une seule ligne, souvent sans contexte. Une méthode plus robuste est nécessaire, et c’est là que les fonctions fenêtres entrent en jeu, telles que RANK() ou ROW_NUMBER(). Ces fonctions permettent de partitionner vos données par groupe, tout en attribuant un numéro de rang à chaque ligne.
Pour illustrer, prenons un exemple. Supposons que vous ayez une table de ventes :
CREATE TABLE ventes (
id INT,
vendeur VARCHAR(255),
montant DECIMAL(10, 2),
mois DATE
);
Si vous voulez obtenir le meilleur vendeur de chaque mois, vous pourriez utiliser la requête suivante :
WITH ClassementVendeurs AS (
SELECT
vendeur,
montant,
mois,
RANK() OVER (PARTITION BY mois ORDER BY montant DESC) AS rang
FROM ventes
)
SELECT vendeur, montant, mois
FROM ClassementVendeurs
WHERE rang = 1;
Dans cet exemple, la fonction RANK() permet de classifier les vendeurs par montant dans chaque mois, tout en gardant la possibilité de voir plusieurs vendeurs en cas d’égalité. Cela vous offre une véritable finesse dans le choix de vos données, sans rien perdre en termes d’information.
Pour résumer, voici un tableau des erreurs fréquentes et des bonnes pratiques lors de la déduplication :
| Erreur fréquente | Bonne pratique |
|---|---|
| Utiliser GROUP BY + LIMIT 1 | Utiliser RANK() ou ROW_NUMBER() pour un classement précis |
| Ignorer les doublons | Prévoir les égalités avec des fonctions fenêtres |
| Perdre des infos contextuelles | Utiliser des partitions pour garder le contexte |
Donc, la prochaine fois que vous vous retrouvez face à ce défi, sachez que vous avez les outils pour y faire face dignement. Cette astuce pourrait même faire la différence lors de votre prochaine interview, alors tenez-la à l’esprit ! Vous pouvez aussi consulter ce fil de discussion pour d’autres conseils utiles.
Alors, prêt à maîtriser ces concepts SQL pour vos entretiens ?
Maîtriser ces six concepts SQL — fonctions fenêtres, clauses HAVING vs WHERE, auto-jointures, CTE, gestion des NULL et déduplication — fait la différence en entretien data. Eviter les pièges révélés ici vous donne un avantage décisif, car ce sont les erreurs les plus communes et sanctionnées. En pratiquant les bonnes pratiques avec les exemples partagés, vous déjouerez les questions piégées et améliorerez votre confiance et vos résultats. Bref, comprendre ces subtilités, c’est s’assurer une place en entretien et dans le business de la data.
FAQ
Quelles fonctions fenêtres sont souvent mal utilisées en SQL ?
Pourquoi ne peut-on pas utiliser les agrégats dans WHERE ?
Quelle est l’avantage d’une auto-jointure par rapport aux sous-requêtes ?
Quand utiliser CTE plutôt que sous-requêtes imbriquées ?
Comment gérer correctement les NULL en SQL ?
A propos de l’auteur
Je suis Franck Scandolera, Analytics Engineer et formateur indépendant depuis plus de 10 ans, spécialisé en SQL, data engineering, et automatisation. Responsable de l’agence webAnalyste et formateur reconnu, j’accompagne professionnels et entreprises à maîtriser les outils et concepts clés pour structurer et exploiter efficacement leurs données. Mon expertise technique s’appuie sur des expériences terrain solides et une pédagogie directe, pour rendre accessible ce qui semble complexe, notamment en SQL et pipelines data.
⭐ 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.






