Comment écrire des requêtes SQL avec plusieurs sous-requêtes combinées

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.

Sommaire

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.

Sommaire