Les sous-requêtes SQL sont une technique puissante permettant d’exécuter une requête à l’intérieur d’une autre. En combinant efficacement plusieurs sous-requêtes, vous pouvez extraire et analyser des données complexes. Cet article explique en détail comment écrire des requêtes SQL utilisant des sous-requêtes, en commençant par les bases, puis en abordant les sous-requêtes corrélées, la combinaison de plusieurs sous-requêtes, et des exemples pratiques. Enfin, des conseils pour optimiser les performances seront également fournis. Si vous souhaitez améliorer vos compétences en SQL, cet article est fait pour vous.
Les bases des sous-requêtes
Une sous-requête est une autre requête SQL incluse dans une instruction SQL. Les sous-requêtes sont utilisées pour fournir des données à la requête principale, et leurs résultats sont exploités dans cette dernière. Voici un exemple de sous-requête basique.
Structure de base
Une sous-requête basique est utilisée dans une instruction SELECT et est entourée de parenthèses. Par exemple, la sous-requête pour trouver l’employé ayant le salaire le plus élevé ressemble à ceci :
SELECT employee_name
FROM employees
WHERE salary = (SELECT MAX(salary) FROM employees);
Types de sous-requêtes
Il existe différents types de sous-requêtes :
- Sous-requête à une ligne : Une sous-requête qui renvoie un seul résultat.
- Sous-requête à plusieurs lignes : Une sous-requête qui renvoie plusieurs résultats.
- Sous-requête corrélée : Une sous-requête qui dépend de chaque ligne de la requête principale.
Comprendre les bases des sous-requêtes est la première étape pour créer des requêtes plus complexes. Passons maintenant aux sous-requêtes corrélées.
Sous-requêtes corrélées
Une sous-requête corrélée est une sous-requête exécutée pour chaque ligne de la requête principale et qui fait référence aux colonnes de celle-ci. Cela permet une extraction de données plus dynamique et flexible.
Concept de sous-requête corrélée
Les sous-requêtes corrélées dépendent de chaque ligne de la requête principale, ce qui permet des comparaisons et des agrégations de données complexes.
Structure de base des sous-requêtes corrélées
Voici la structure de base d’une sous-requête corrélée. Dans l’exemple ci-dessous, on extrait les employés dont le salaire est supérieur à la moyenne des salaires dans leur département.
SELECT employee_name, salary
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e1.department_id = e2.department_id
);
Dans cette requête, la requête extérieure (requête principale) et la requête intérieure (sous-requête) sont liées par le department_id
, et vérifient si le salaire de chaque employé dépasse la moyenne du département.
Avantages des sous-requêtes corrélées
Les sous-requêtes corrélées présentent plusieurs avantages :
- Flexibilité : Elles permettent de définir des conditions complexes et de faire des calculs ou des comparaisons spécifiques à chaque ligne de la requête principale.
- Extraction de données dynamique : Elles génèrent des résultats dynamiques basés sur les données de la requête principale.
Passons maintenant à la manière de combiner plusieurs sous-requêtes pour extraire des données complexes.
Comment combiner plusieurs sous-requêtes
En combinant plusieurs sous-requêtes, vous pouvez extraire et analyser des données très complexes et détaillées. Voici comment utiliser plusieurs sous-requêtes efficacement.
Sous-requêtes imbriquées
Vous pouvez imbriquer une sous-requête à l’intérieur d’une autre pour réaliser des extractions de données hiérarchiques. L’exemple ci-dessous montre comment obtenir le nom du département de l’employé ayant le salaire le plus élevé.
SELECT department_name
FROM departments
WHERE department_id = (
SELECT department_id
FROM employees
WHERE salary = (
SELECT MAX(salary)
FROM employees
)
);
Cette requête identifie l’employé ayant le salaire le plus élevé, puis récupère le nom du département auquel il appartient.
Combinaison avec une sous-requête corrélée
Vous pouvez également combiner des sous-requêtes corrélées avec d’autres sous-requêtes. Dans l’exemple ci-dessous, on vérifie si le salaire de chaque employé est supérieur à la moyenne du département, puis on récupère le nom du département correspondant.
SELECT employee_name, department_name
FROM employees e1
JOIN departments d ON e1.department_id = d.department_id
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e1.department_id = e2.department_id
);
Cette requête utilise une sous-requête corrélée pour vérifier si le salaire de chaque employé dépasse la moyenne de son département, puis récupère le nom du département correspondant.
Sous-requêtes avec plusieurs clauses WITH
Lorsque vous utilisez plusieurs sous-requêtes, les clauses WITH (expressions de table communes, CTE) peuvent rendre vos requêtes plus lisibles et plus faciles à maintenir. Voici un exemple utilisant une CTE.
WITH MaxSalary AS (
SELECT department_id, MAX(salary) AS max_salary
FROM employees
GROUP BY department_id
),
HighEarners AS (
SELECT e.employee_name, d.department_name, e.salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN MaxSalary m ON e.department_id = m.department_id AND e.salary = m.max_salary
)
SELECT * FROM HighEarners;
Cette requête commence par identifier les employés ayant les salaires les plus élevés dans chaque département, puis sélectionne les noms de ces employés et les noms des départements correspondants.
Passons maintenant à des exemples pratiques utilisant des sous-requêtes imbriquées.
Exemple pratique 1 : Sous-requêtes imbriquées
Les sous-requêtes imbriquées sont utilisées pour réaliser des extractions de données hiérarchiques. Cette section explique comment les utiliser à travers un exemple pratique.
Exemple : Obtenir l’employé ayant le salaire le plus élevé dans un département spécifique
Dans cet exemple, nous allons récupérer l’employé ayant le salaire le plus élevé dans un département spécifique (par exemple, le département avec l’ID 5).
SELECT employee_name, salary
FROM employees
WHERE salary = (
SELECT MAX(salary)
FROM employees
WHERE department_id = 5
);
Cette requête commence par trouver le salaire le plus élevé parmi les employés du département ayant l’ID 5, puis récupère le nom et le salaire de l’employé correspondant.
Exemple : Obtenir l’employé avec le salaire le plus élevé dans chaque département
Pour un exemple plus complexe, nous allons montrer comment récupérer l’employé ayant le salaire le plus élevé dans chaque département.
SELECT employee_name, department_id, salary
FROM employees e1
WHERE salary = (
SELECT MAX(salary)
FROM employees e2
WHERE e1.department_id = e2.department_id
);
Cette requête utilise une sous-requête corrélée pour identifier l’employé ayant le salaire le plus élevé dans chaque département. La sous-requête interne récupère le salaire maximum basé sur le department_id
transmis par la requête externe, qui sélectionne ensuite l’employé correspondant.
Exemple : Obtenir les détails de l’employé avec le salaire le plus élevé dans chaque département
Enfin, voici comment récupérer les détails (nom, salaire, nom du département) de l’employé ayant le salaire le plus élevé dans chaque département.
SELECT e1.employee_name, e1.salary, d.department_name
FROM employees e1
JOIN departments d ON e1.department_id = d.department_id
WHERE e1.salary = (
SELECT MAX(e2.salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
);
Cette requête identifie d’abord l’employé avec le salaire maximum dans chaque département, puis récupère les informations détaillées de cet employé, y compris le nom du département.
Voyons maintenant comment organiser des sous-requêtes complexes avec la clause WITH.
Exemple pratique 2 : Sous-requête avec clause WITH
L’utilisation de la clause WITH (expression de table commune, CTE) permet d’organiser des requêtes complexes de manière plus lisible et plus facile à maintenir. Cette section présente comment utiliser la clause WITH pour simplifier des sous-requêtes à travers des exemples pratiques.
Exemple : Obtenir l’employé avec le salaire le plus élevé dans chaque département
Commençons par montrer comment récupérer l’employé ayant le salaire le plus élevé dans chaque département en utilisant la clause WITH.
WITH MaxSalaries AS (
SELECT department_id, MAX(salary) AS max_salary
FROM employees
GROUP BY department_id
)
SELECT e.employee_name, e.salary, d.department_name
FROM employees e
JOIN MaxSalaries m ON e.department_id = m.department_id AND e.salary = m.max_salary
JOIN departments d ON e.department_id = d.department_id;
Dans cette requête, la CTE nommée MaxSalaries calcule le salaire maximum dans chaque département, qui est ensuite utilisé dans la requête principale. Cela améliore la lisibilité de l’ensemble de la requête.
Exemple : Obtenir le meilleur vendeur
Ensuite, voici un exemple pour obtenir les informations détaillées du vendeur ayant réalisé le plus de ventes.
WITH SalesData AS (
SELECT salesperson_id, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY salesperson_id
),
TopSalesperson AS (
SELECT salesperson_id, MAX(total_sales) AS max_sales
FROM SalesData
)
SELECT s.salesperson_name, sd.total_sales
FROM SalesData sd
JOIN TopSalesperson ts ON sd.salesperson_id = ts.salesperson_id AND sd.total_sales = ts.max_sales
JOIN salespersons s ON sd.salesperson_id = s.salesperson_id;
Dans cette requête, la CTE SalesData calcule les ventes totales de chaque vendeur, et la CTE TopSalesperson identifie le vendeur ayant le plus de ventes. Enfin, la requête principale récupère les informations détaillées de ce vendeur.
Exemple : Obtenir les ventes moyennes de chaque mois pour une année spécifique
Enfin, voici comment obtenir les ventes moyennes de chaque mois pour une année spécifique en utilisant la clause WITH.
WITH MonthlySales AS (
SELECT DATE_TRUNC('month', sale_date) AS month, AVG(sales_amount) AS avg_sales
FROM sales
WHERE EXTRACT(year FROM sale_date) = 2023
GROUP BY DATE_TRUNC('month', sale_date)
)
SELECT month, avg_sales
FROM MonthlySales
ORDER BY month;
Dans cette requête, la CTE MonthlySales calcule les ventes moyennes pour chaque mois de l’année 2023, et la requête principale utilise ce résultat pour extraire les moyennes de chaque mois.
Voyons maintenant quelques conseils et techniques pour optimiser les performances des requêtes SQL utilisant des sous-requêtes.
Optimisation des performances
Les requêtes SQL utilisant des sous-requêtes sont puissantes, mais elles peuvent également poser des problèmes de performances. Voici quelques conseils et techniques pour optimiser les performances de ces requêtes.
Utilisation des index
Créer des index sur les colonnes utilisées dans les sous-requêtes peut améliorer considérablement la vitesse d’exécution des requêtes. Il est particulièrement important de créer des index sur les colonnes fréquemment utilisées dans les sous-requêtes.
CREATE INDEX idx_department_id ON employees(department_id);
Dans cet exemple, un index est créé sur la colonne department_id
pour accélérer les recherches.
Éviter les sous-requêtes inutiles
Certaines sous-requêtes sont redondantes et peuvent être simplifiées en utilisant un JOIN. En éliminant les sous-requêtes non nécessaires, vous pouvez améliorer les performances de vos requêtes.
-- Exemple avec sous-requête
SELECT e.employee_name, d.department_name
FROM employees e
WHERE e.department_id = (SELECT department_id FROM departments WHERE department_name = 'Sales');
-- Exemple optimisé avec JOIN
SELECT e.employee_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE d.department_name = 'Sales';
Dans cet exemple, la sous-requête est remplacée par un JOIN, ce qui rend la requête plus simple et plus rapide.
Optimisation avec EXISTS
Lorsque vous utilisez le résultat d’une sous-requête pour vérifier l’existence d’une condition, l’utilisation de l’opérateur EXISTS
peut améliorer les performances. EXISTS
termine le traitement dès qu’une ligne correspondant à la condition est trouvée, ce qui le rend efficace.
-- Exemple avec sous-requête
SELECT employee_name
FROM employees
WHERE department_id IN (SELECT department_id FROM departments WHERE location = 'New York');
-- Exemple optimisé avec EXISTS
SELECT employee_name
FROM employees e
WHERE EXISTS (
SELECT 1
FROM departments d
WHERE e.department_id = d.department_id AND d.location = 'New York'
);
Dans cet exemple, l’opérateur IN
est remplacé par EXISTS
, ce qui améliore l’efficacité de l’exécution de la requête.
Utilisation des vues
Si vous exécutez fréquemment des requêtes incluant des sous-requêtes complexes, convertir ces sous-requêtes en vues peut améliorer les performances. Les vues permettent de sauvegarder les résultats des requêtes comme des tables virtuelles, ce qui facilite leur réutilisation.
-- Création d'une vue
CREATE VIEW HighSalaryEmployees AS
SELECT employee_name, salary, department_id
FROM employees
WHERE salary > 100000;
-- Requête utilisant la vue
SELECT e.employee_name, d.department_name
FROM HighSalaryEmployees e
JOIN departments d ON e.department_id = d.department_id;
Dans cet exemple, une vue appelée HighSalaryEmployees
est créée, puis utilisée pour simplifier et améliorer la lisibilité et les performances de la requête.
Mise à jour des statistiques
Mettre à jour régulièrement les statistiques de la base de données aide l’optimiseur de requêtes à générer des plans d’exécution optimaux. Les statistiques incluent des informations sur les index et la cardinalité des tables.
-- Mise à jour des statistiques (exemple : PostgreSQL)
ANALYZE employees;
Dans cet exemple, les statistiques de la table employees
sont mises à jour pour optimiser les performances des requêtes.
Conclusion
Dans cet article, nous avons expliqué comment écrire des requêtes SQL utilisant plusieurs sous-requêtes combinées. Nous avons abordé les bases des sous-requêtes, les sous-requêtes corrélées, la combinaison de plusieurs sous-requêtes, les sous-requêtes imbriquées et les exemples pratiques utilisant la clause WITH, ainsi que des conseils pour optimiser les performances. En utilisant ces techniques, vous serez en mesure de créer des requêtes SQL plus efficaces et puissantes. Améliorez vos compétences en SQL et appliquez ces techniques pour extraire et analyser des données complexes.