Questions d'entretien SQL
Les entretiens SQL testent votre capacité à exprimer des questions de données correctement et efficacement. Attendez-vous à écrire des requêtes en direct, impliquant souvent des jointures, des agrégations et des fonctions de fenêtre.
Ce que couvrent les entretiens SQL
Jointures et filtrage
Jointures INNER/LEFT/RIGHT/FULL, WHERE vs HAVING, et auto-jointures.
Agrégation
GROUP BY, fonctions d'agrégation et pièges du regroupement (NULL, doublons).
Fonctions de fenêtre
ROW_NUMBER, RANK, LAG/LEAD, totaux cumulés et top-N par groupe.
Performance
Index, plans de requête, sargabilité et pourquoi une requête est lente.
Exemples de questions d'entretien SQL
- Trouvez le deuxième salaire le plus élevé dans une table.Ce qu'une bonne réponse couvre
- Utiliser DISTINCT pour gérer les égalités
- Sous-requête avec ORDER BY et LIMIT/OFFSET
- Fonction de fenêtre DENSE_RANK pour robustesse
- Gestion des NULLs
Voir un exemple de réponse
Pour trouver le deuxième salaire le plus élevé, on peut utiliser une sous-requête avec ORDER BY DESC et LIMIT 1 OFFSET 1. Cependant, cela échoue en cas d'égalité. Une meilleure approche utilise DISTINCT pour obtenir des salaires uniques, puis LIMIT/OFFSET. Alternativement, utilisez DENSE_RANK() : SELECT salary FROM (SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk FROM employees) WHERE rnk = 2. Cette méthode gère correctement les ex-aequo. Attention : si le tableau contient moins de deux salaires distincts, la requête ne retourne rien. En termes de performances, un index sur salary accélère le tri. Complexité temporelle : O(n log n) pour le tri, O(n) avec un index B-tree.
Solution de référencesql -- Méthode robuste avec DENSE_RANK SELECT DISTINCT salary FROM ( SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk FROM employees ) sub WHERE rnk = 2; -- Alternative avec sous-requête et DISTINCT SELECT DISTINCT salary FROM employees ORDER BY salary DESC LIMIT 1 OFFSET 1; - Obtenez les N premières lignes par groupe (ex : top 3 produits par catégorie).Ce qu'une bonne réponse couvre
- Utilisation de ROW_NUMBER() ou RANK()
- Partitionnement par catégorie
- Gestion des égalités avec RANK vs DENSE_RANK
- Sous-requête corrélée comme alternative
Voir un exemple de réponse
Pour obtenir les N premières lignes par groupe, la méthode la plus efficace utilise une fonction de fenêtre comme ROW_NUMBER() ou RANK(). On partitionne par catégorie (PARTITION BY category) et on trie par la métrique souhaitée (ORDER BY metric DESC). Ensuite, on filtre sur le rang <= N dans une sous-requête ou un CTE. ROW_NUMBER() attribue des rangs uniques même en cas d'égalité, tandis que RANK() conserve les ex-aequo. Par exemple, pour le top 3 des produits par catégorie de ventes : WITH ranked AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS rn FROM products) SELECT * FROM ranked WHERE rn <= 3. Une alternative est une sous-requête corrélée avec COUNT, mais moins performante. Attention : si N est grand, l'opération peut être coûteuse ; un index sur (category, sales DESC) améliore les performances. Complexité : O(n log n) par groupe avec tri.
Solution de référencesql -- Top 3 produits par catégorie WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS rn FROM products ) SELECT * FROM ranked WHERE rn <= 3; -- Alternative avec sous-requête corrélée (moins performante) SELECT p.* FROM products p WHERE ( SELECT COUNT(*) FROM products p2 WHERE p2.category = p.category AND p2.sales > p.sales ) < 3; - Expliquez la différence entre WHERE et HAVING.Ce qu'une bonne réponse couvre
- Filtrage avant agrégation (WHERE)
- Filtrage après agrégation (HAVING)
- WHERE ne peut pas utiliser d'agrégats
- Ordre d'exécution dans une requête SQL
Voir un exemple de réponse
WHERE et HAVING filtrent tous deux des lignes, mais à des étapes différentes de l'exécution. WHERE s'applique avant le regroupement (GROUP BY) et ne peut pas contenir de fonctions d'agrégation (comme SUM, COUNT). Il filtre des lignes individuelles de la table. HAVING s'applique après le regroupement et peut utiliser des agrégats. Par exemple, pour trouver les départements avec plus de 5 employés : SELECT dept_id, COUNT(*) FROM employees GROUP BY dept_id HAVING COUNT(*) > 5. Si on ajoute un filtre sur le salaire avant le groupement, on utilise WHERE : WHERE salary > 50000. L'ordre logique d'exécution est : FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. Utiliser WHERE plutôt que HAVING pour les filtres non agrégés est plus efficace, car il réduit le nombre de lignes à agréger. Attention : ne pas confondre HAVING avec une clause de filtrage sur un champ non agrégé ; cela fonctionne mais est déconseillé.
- Écrivez une requête pour trouver les lignes en double et n'en conserver qu'une seule.Ce qu'une bonne réponse couvre
- Identifier les doublons avec GROUP BY et COUNT > 1
- Utiliser une colonne unique ou CTID pour supprimer
- Fonction de fenêtre ROW_NUMBER() pour choisir une ligne
- Préserver une ligne par groupe avec MIN/MAX
Voir un exemple de réponse
Pour trouver les lignes en double, on groupe par les colonnes censées être uniques et on conserve celles avec COUNT(*) > 1. Pour n'en conserver qu'une seule, on utilise une sous-requête avec ROW_NUMBER() partitionnée par les colonnes de duplication, ordonnée par une colonne discriminante (ex : id). On supprime ensuite les lignes où le rang > 1. Exemple : DELETE FROM table WHERE (col1, col2) IN (SELECT col1, col2 FROM table GROUP BY col1, col2 HAVING COUNT(*) > 1) AND id NOT IN (SELECT MIN(id) FROM table GROUP BY col1, col2). Ou avec une CTE : WITH cte AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY id) AS rn FROM table) DELETE FROM table WHERE (col1, col2) IN (SELECT col1, col2 FROM cte WHERE rn > 1) AND id NOT IN (SELECT id FROM cte WHERE rn = 1). Attention : la suppression en masse peut bloquer la table ; tester sur une copie. Complexité : O(n log n) dû au tri.
Solution de référencesql -- Identifier les doublons SELECT col1, col2, COUNT(*) FROM table GROUP BY col1, col2 HAVING COUNT(*) > 1; -- Supprimer les doublons en gardant la ligne avec le plus petit id WITH cte AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY id) AS rn FROM table ) DELETE FROM table WHERE id IN (SELECT id FROM cte WHERE rn > 1); -- Alternative avec MIN DELETE FROM table WHERE id NOT IN ( SELECT MIN(id) FROM table GROUP BY col1, col2 ); - Calculez un total cumulé des revenus journaliers.Ce qu'une bonne réponse couvre
- Fonction de fenêtre SUM() OVER (ORDER BY date)
- Rangées précédentes incluses par défaut
- Gestion des dates manquantes
- Alternative avec jointure auto (moins efficace)
Voir un exemple de réponse
Pour calculer un total cumulé des revenus journaliers, on utilise une fonction de fenêtre SUM(revenue) OVER (ORDER BY date) qui ajoute le revenu de chaque jour au total précédent. Par défaut, le cadre de la fenêtre inclut toutes les lignes du début de la partition jusqu'à la ligne courante. Exemple : SELECT date, revenue, SUM(revenue) OVER (ORDER BY date) AS cumulative_revenue FROM daily_revenue. Si les dates ne sont pas consécutives, cela donne un cumul correct. Pour éviter les lacunes, on peut d'abord générer une séquence de dates avec une LEFT JOIN. Une autre approche moins performante est une sous-requête corrélée : SELECT date, (SELECT SUM(revenue) FROM daily_revenue r2 WHERE r2.date <= r.date) FROM daily_revenue r. La fonction de fenêtre est beaucoup plus efficace (O(n log n) contre O(n²)). Attention : si le volume de données est très grand, s'assurer que la table est indexée par date pour éviter un tri explicite. Complexité : O(n log n) avec tri, O(n) si pré-trier.
Solution de référencesql -- Total cumulé des revenus journaliers SELECT date, revenue, SUM(revenue) OVER (ORDER BY date) AS cumulative_revenue FROM daily_revenue ORDER BY date; -- Alternative avec jointure auto (moins performante) SELECT r1.date, r1.revenue, SUM(r2.revenue) AS cumulative_revenue FROM daily_revenue r1 JOIN daily_revenue r2 ON r2.date <= r1.date GROUP BY r1.date, r1.revenue ORDER BY r1.date; - Comment accéléreriez-vous une requête lente ? Que vous dit le plan de requête ?Ce qu'une bonne réponse couvre
- Analyser le plan d'exécution avec EXPLAIN
- Index manquants ou sous-optimaux
- Réécriture de requête (sous-requêtes vs jointures)
- Partitionnement et statistiques
- Limiter les colonnes et les lignes inutiles
Voir un exemple de réponse
Pour accélérer une requête lente, commencez par analyser le plan d'exécution avec EXPLAIN (ou EXPLAIN ANALYZE). Le plan montre les opérations effectuées (sequential scan, index scan, join types) et leurs coûts. Recherchez les points coûteux : full table scans sur de grandes tables indiquent un besoin d'index. Créez des indexes sur les colonnes utilisées dans WHERE, JOIN et ORDER BY. Pour les requêtes avec plusieurs conditions, un index composé peut être plus efficace. Ensuite, examinez la requête elle-même : évitez les sous-requêtes non corrélées quand une jointure est possible, utilisez des CTE si nécessaire. Vérifiez que les statistiques sont à jour (ANALYZE). Pour les agrégations lourdes, envisagez des index couvrants ou des tables résumées. Le plan de requête peut aussi révéler des types de jointure inefficaces (Nested Loop vs Hash Join). Attention : trop d'index ralentissent les écritures, donc équilibrez. Un follow-up typique : 'Combien de temps la requête prend-elle ?' et 'Quelle est la taille des tables ?'.
Comment se préparer
- Entraînez-vous à écrire des requêtes à la main — de nombreux entretiens utilisent un éditeur partagé sans autocomplétion.
- Maîtrisez les fonctions de fenêtre ; elles débloquent une grande classe de problèmes de top-N par groupe et de totaux cumulés.
- Réfléchissez explicitement au comportement des NULL et des doublons — ce sont des pièges courants.
- Soyez prêt à lire un plan de requête et à expliquer pourquoi un index est utile.
Questions fréquemment posées
Quel dialecte SQL dois-je étudier ?
Le SQL standard couvre la plupart des entretiens. Sachez que les fonctions de fenêtre et les CTE sont largement supportés ; certaines fonctions diffèrent entre PostgreSQL, MySQL et SQL Server.
Dois-je optimiser les requêtes lors d'un entretien SQL ?
Pour les postes de données/backend, oui — soyez prêt à discuter des index, des plans de requête et des raisons de lenteur d'une requête.
Les fonctions de fenêtre sont-elles couramment demandées ?
Très. ROW_NUMBER/RANK et les totaux cumulés apparaissent constamment, surtout pour les rôles d'ingénierie des données et d'analyse.
Comment puis-je m'entraîner aux entretiens SQL ?
Résolvez des problèmes de requêtes sur un schéma réel et expliquez votre approche. Offersly peut générer des questions SQL et évaluer votre raisonnement.
Pratiquez les questions SQL avec des retours instantanés de l'IA
Téléchargez votre CV, obtenez un entretien simulé personnalisé et voyez exactement ce qu'il faut améliorer — gratuit pour commencer.