Sous-requêtes et opérations imbriquées MySQL

MySQLBeginner
Pratiquer maintenant

Introduction

Dans ce laboratoire, vous explorerez la puissance des sous-requêtes MySQL et des opérations imbriquées. L'objectif principal est d'utiliser des sous-requêtes dans la clause WHERE pour filtrer les données en fonction de conditions dérivées d'autres tables ou de la même table.

Vous apprendrez à vous connecter à un serveur MySQL, à créer une base de données et des tables (clients et commandes), puis à construire des requêtes SQL qui utilisent des sous-requêtes pour identifier les clients ayant passé des commandes dont le montant total dépasse une valeur spécifique. Le laboratoire couvre également l'utilisation de EXISTS avec une sous-requête, le test des sous-requêtes corrélées et la comparaison des performances des sous-requêtes.

Écrire une sous-requête dans une clause WHERE

Dans cette étape, vous apprendrez à utiliser une sous-requête dans la clause WHERE d'une instruction SQL. Une sous-requête est une requête imbriquée dans une autre requête. C'est un outil puissant pour récupérer des données basées sur des conditions dérivées d'autres tables ou de la même table.

Comprendre les sous-requêtes

Une sous-requête (ou requête interne) est une requête SQL imbriquée dans une requête plus grande. La sous-requête est exécutée en premier, et son résultat est utilisé par la requête externe. Les sous-requêtes peuvent apparaître dans les clauses WHERE, SELECT, FROM et HAVING.

Dans la clause WHERE, une sous-requête est généralement utilisée pour filtrer les résultats de la requête externe en fonction d'une condition. La sous-requête renvoie une seule valeur ou un ensemble de valeurs que la requête externe utilise pour la comparaison.

Scénario

Imaginez que vous avez deux tables : customers et orders. La table customers contient les informations sur les clients (par exemple, customer_id, name, city), et la table orders contient les informations sur les commandes (par exemple, order_id, customer_id, order_date, total_amount).

Vous souhaitez trouver tous les clients qui ont passé au moins une commande dont le montant total est supérieur à 100 $.

Étapes

  1. Se connecter au serveur MySQL :

    Ouvrez votre terminal et exécutez la commande suivante pour vous connecter à votre serveur MySQL en tant qu'utilisateur root :

    sudo mysql -u root

    Vous devriez voir l'invite MySQL : mysql>.

  2. Créer la base de données et les tables :

    Si vous n'avez pas encore de base de données et de tables, créez-les maintenant. Créons une base de données nommée labdb et les tables customers et orders. Exécutez les commandes SQL suivantes dans l'invite MySQL :

    CREATE DATABASE IF NOT EXISTS labdb;
    USE labdb;
    
    CREATE TABLE IF NOT EXISTS customers (
        customer_id INT PRIMARY KEY,
        name VARCHAR(255),
        city VARCHAR(255)
    );
    
    CREATE TABLE IF NOT EXISTS orders (
        order_id INT PRIMARY KEY AUTO_INCREMENT,
        customer_id INT,
        order_date DATE,
        total_amount DECIMAL(10, 2),
        FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
    );
  3. Insérer des données d'exemple :

    Insérez des données d'exemple dans les tables. Exécutez les commandes SQL suivantes dans l'invite MySQL :

    INSERT INTO customers (customer_id, name, city) VALUES
    (1, 'Alice Smith', 'New York'),
    (2, 'Bob Johnson', 'Los Angeles'),
    (3, 'Charlie Brown', 'Chicago'),
    (4, 'David Lee', 'Houston');
    
    INSERT INTO orders (customer_id, order_date, total_amount) VALUES
    (1, '2023-01-15', 120.00),
    (2, '2023-02-20', 80.00),
    (1, '2023-03-10', 150.00),
    (3, '2023-04-05', 200.00),
    (2, '2023-05-12', 110.00),
    (4, '2023-06-18', 90.00);
  4. Écrire la sous-requête dans la clause WHERE :

    Maintenant, écrivez la requête pour trouver les clients qui ont passé des commandes avec un montant total supérieur à 100 $. Exécutez la commande SQL suivante dans l'invite MySQL :

    SELECT *
    FROM customers
    WHERE customer_id IN (SELECT customer_id FROM orders WHERE total_amount > 100);

    Explication :

    • La sous-requête (SELECT customer_id FROM orders WHERE total_amount > 100) sélectionne le customer_id de la table orders où le total_amount est supérieur à 100.
    • La requête externe SELECT * FROM customers WHERE customer_id IN (...) sélectionne toutes les colonnes de la table customers où le customer_id fait partie de l'ensemble des customer_id renvoyés par la sous-requête.
  5. Observer le résultat :

    Vous devriez voir le résultat suivant, affichant les clients qui ont passé des commandes avec un montant total supérieur à 100 $ :

    +-------------+-------------+-----------+
    | customer_id | name        | city      |
    +-------------+-------------+-----------+
    |           1 | Alice Smith | New York  |
    |           2 | Bob Johnson | Los Angeles |
    |           3 | Charlie Brown | Chicago   |
    +-------------+-------------+-----------+
    3 rows in set (0.00 sec)

Utiliser EXISTS avec une sous-requête

Dans cette étape, vous apprendrez à utiliser l'opérateur EXISTS avec une sous-requête en MySQL. L'opérateur EXISTS est utilisé pour tester l'existence de lignes dans une sous-requête. Il renvoie TRUE si la sous-requête renvoie des lignes, et FALSE sinon.

Comprendre EXISTS

L'opérateur EXISTS est souvent utilisé dans la clause WHERE d'une instruction SQL pour filtrer les résultats en fonction de l'existence de données associées dans une autre table. C'est une alternative puissante à l'utilisation des opérations IN ou JOIN, et peut parfois être plus efficace, en particulier lors du traitement de grands ensembles de données.

Contrairement à IN, EXISTS ne récupère pas réellement les données de la sous-requête. Il vérifie simplement si des lignes sont renvoyées. Cela peut le rendre plus rapide que IN lorsque vous avez seulement besoin de savoir si une correspondance existe, et non les valeurs réelles.

Scénario

En continuant avec les tables customers et orders de l'étape précédente, trouvons tous les clients qui ont passé au moins une commande.

Prérequis

Assurez-vous d'avoir terminé l'étape précédente ("Écrire une sous-requête dans une clause WHERE") et que la base de données labdb, la table customers et la table orders sont remplies de données.

Étapes

  1. Écrire la requête utilisant EXISTS :

    Écrivez la requête pour trouver les clients qui ont passé au moins une commande. Exécutez la commande SQL suivante dans l'invite MySQL :

    SELECT *
    FROM customers c
    WHERE EXISTS (
        SELECT 1
        FROM orders o
        WHERE o.customer_id = c.customer_id
    );

    Explication :

    • La requête externe SELECT * FROM customers c sélectionne toutes les colonnes de la table customers, avec l'alias c.
    • La clause WHERE EXISTS (...) vérifie si la sous-requête renvoie des lignes.
    • La sous-requête SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id sélectionne la valeur 1 (cela pourrait être n'importe quelle valeur constante) de la table orders, avec l'alias o, où le customer_id dans la table orders correspond au customer_id dans la table customers.
    • L'opérateur EXISTS renvoie TRUE si la sous-requête renvoie au moins une ligne, indiquant que le client a passé au moins une commande.
  2. Observer le résultat :

    Vous devriez voir le résultat suivant, affichant tous les clients qui ont passé au moins une commande :

    +-------------+-------------+-----------+
    | customer_id | name        | city      |
    +-------------+-------------+-----------+
    |           1 | Alice Smith | New York  |
    |           2 | Bob Johnson | Los Angeles |
    |           3 | Charlie Brown | Chicago   |
    |           4 | David Lee   | Houston   |
    +-------------+-------------+-----------+
    4 rows in set (0.00 sec)
  3. Modifier la requête (facultatif) :

    Modifions la requête pour trouver les clients qui n'ont passé aucune commande. Vous pouvez le faire en utilisant NOT EXISTS. Exécutez la commande SQL suivante dans l'invite MySQL :

    SELECT *
    FROM customers c
    WHERE NOT EXISTS (
        SELECT 1
        FROM orders o
        WHERE o.customer_id = c.customer_id
    );
  4. Observer le résultat :

    Étant donné que tous les clients de nos données d'exemple ont passé des commandes, cette requête devrait renvoyer un ensemble de résultats vide :

    Empty set (0.00 sec)

Tester une sous-requête corrélée

Dans cette étape, vous apprendrez les sous-requêtes corrélées en MySQL. Une sous-requête corrélée est une sous-requête qui référence une colonne de la requête externe. Cela signifie que la sous-requête est exécutée une fois pour chaque ligne de la requête externe.

Comprendre les sous-requêtes corrélées

Contrairement à une sous-requête simple, qui n'est exécutée qu'une seule fois, une sous-requête corrélée dépend de la requête externe pour ses valeurs. La sous-requête utilise les valeurs de la ligne actuelle de la requête externe pour déterminer son résultat. Cela rend les sous-requêtes corrélées plus puissantes pour certains types de requêtes, mais aussi potentiellement moins efficaces que les sous-requêtes simples, en particulier pour les grands ensembles de données.

Scénario

En continuant avec les tables customers et orders, trouvons tous les clients qui ont passé une commande dont le montant est supérieur au montant moyen de toutes les commandes.

Prérequis

Assurez-vous d'avoir terminé les étapes précédentes et que la base de données labdb, la table customers et la table orders sont remplies de données.

Étapes

  1. Écrire la sous-requête corrélée :

    Écrivez la requête pour trouver les clients qui ont passé une commande dont le montant est supérieur au montant moyen. Exécutez la commande SQL suivante dans l'invite MySQL :

    SELECT c.customer_id, c.name
    FROM customers c
    WHERE EXISTS (
        SELECT 1
        FROM orders o
        WHERE o.customer_id = c.customer_id
        AND o.total_amount > (SELECT AVG(total_amount) FROM orders)
    );

    Explication :

    • La requête externe SELECT c.customer_id, c.name FROM customers c sélectionne le customer_id et le name de la table customers, avec l'alias c.
    • La clause WHERE EXISTS (...) vérifie si la sous-requête renvoie des lignes.
    • La sous-requête corrélée SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.total_amount > (SELECT AVG(total_amount) FROM orders) sélectionne la valeur 1 de la table orders, avec l'alias o, où :
      • o.customer_id = c.customer_id : C'est la corrélation. La sous-requête référence le customer_id de la table customers de la requête externe.
      • o.total_amount > (SELECT AVG(total_amount) FROM orders) : Cette condition vérifie si le montant de la commande est supérieur au montant moyen de toutes les commandes. La sous-requête AVG(total_amount) est une sous-requête non corrélée qui est exécutée une seule fois pour obtenir le montant moyen des commandes.
  2. Observer le résultat :

    Vous devriez voir le résultat suivant, affichant les clients qui ont passé des commandes dont le montant est supérieur au montant moyen :

    +-------------+-------------+
    | customer_id | name        |
    +-------------+-------------+
    |           1 | Alice Smith |
    |           3 | Charlie Brown |
    +-------------+-------------+
    2 rows in set (0.00 sec)
  3. Autre exemple : trouver le montant de commande le plus élevé pour chaque client

    Exécutez la commande SQL suivante dans l'invite MySQL pour récupérer l'ID, le nom de chaque client et leur montant de commande le plus élevé. La sous-requête corrélée calcule le montant maximum de commande pour chaque client individuellement.

    SELECT c.customer_id, c.name, (
        SELECT MAX(o.total_amount)
        FROM orders o
        WHERE o.customer_id = c.customer_id
    ) AS highest_order_amount
    FROM customers c;
  4. Observer le résultat :

    Vous devriez voir le résultat suivant :

    +-------------+-------------+-----------------------+
    | customer_id | name        | highest_order_amount  |
    +-------------+-------------+-----------------------+
    |           1 | Alice Smith |                150.00 |
    |           2 | Bob Johnson |                110.00 |
    |           3 | Charlie Brown |                200.00 |
    |           4 | David Lee   |                 90.00 |
    +-------------+-------------+-----------------------+
    4 rows in set (0.00 sec)

Comparer les performances des sous-requêtes

Dans cette étape, vous apprendrez à comparer les performances de différents types de sous-requêtes en MySQL. Comprendre les caractéristiques de performance des sous-requêtes est crucial pour écrire des requêtes SQL efficaces, en particulier lors du traitement de grands ensembles de données.

Comprendre les considérations de performance

Les performances d'une sous-requête peuvent être affectées par plusieurs facteurs, notamment :

  • Taille des données : La taille des tables impliquées dans la requête.
  • Type de sous-requête : Si la sous-requête est corrélée ou non corrélée.
  • Indexation : La présence et l'efficacité des index sur les tables.
  • Version de MySQL : La version spécifique de MySQL utilisée, car les techniques d'optimisation des requêtes peuvent varier.

Scénario

En continuant avec les tables customers et orders, comparons les performances d'une sous-requête utilisant IN par rapport à une sous-requête utilisant EXISTS pour trouver tous les clients qui ont passé au moins une commande.

Prérequis

Assurez-vous d'avoir terminé les étapes précédentes et que la base de données labdb, la table customers et la table orders sont remplies de données. Pour rendre la comparaison de performance plus significative, nous ajouterons plus de données à la table orders.

Étapes

  1. Ajouter plus de données à la table orders :

    Pour rendre la comparaison de performance plus réaliste, ajoutons une quantité significative de données à la table orders. Nous insérerons 1000 commandes pour chaque client à l'aide d'une procédure stockée. Exécutez les commandes SQL suivantes dans l'invite MySQL :

    DELIMITER //
    CREATE PROCEDURE insert_many_orders()
    BEGIN
      DECLARE i INT DEFAULT 1;
      WHILE i <= 1000 DO
        INSERT INTO orders (customer_id, order_date, total_amount) VALUES (1, CURDATE(), 50.00);
        INSERT INTO orders (customer_id, order_date, total_amount) VALUES (2, CURDATE(), 75.00);
        INSERT INTO orders (customer_id, order_date, total_amount) VALUES (3, CURDATE(), 100.00);
        INSERT INTO orders (customer_id, order_date, total_amount) VALUES (4, CURDATE(), 125.00);
        SET i = i + 1;
      END WHILE;
    END//
    DELIMITER ;
    CALL insert_many_orders();
    DROP PROCEDURE insert_many_orders;

    Explication :

    • Ce script SQL crée une procédure stockée nommée insert_many_orders.
    • La procédure insère 1000 commandes pour chacun des quatre clients dans la table orders.
    • Après l'insertion des données, la procédure est supprimée.
  2. Requête utilisant IN :

    Exécutez la requête suivante utilisant IN pour trouver tous les clients qui ont passé au moins une commande :

    SELECT *
    FROM customers
    WHERE customer_id IN (SELECT customer_id FROM orders);
  3. Analyser le plan d'exécution de la requête à l'aide de EXPLAIN :

    Avant d'exécuter la requête, utilisez la commande EXPLAIN pour analyser le plan d'exécution de la requête. Cela vous donnera un aperçu de la manière dont MySQL prévoit d'exécuter la requête et identifiera les goulots d'étranglement potentiels en matière de performance. Exécutez la commande SQL suivante dans l'invite MySQL :

    EXPLAIN SELECT *
    FROM customers
    WHERE customer_id IN (SELECT customer_id FROM orders);

    La sortie de EXPLAIN vous montrera les tables accédées, les index utilisés (le cas échéant) et l'ordre dans lequel les opérations sont effectuées. Portez une attention particulière à la colonne type, qui indique le type de jointure ou de méthode d'accès utilisée.

  4. Requête utilisant EXISTS :

    Exécutez la requête suivante utilisant EXISTS pour trouver tous les clients qui ont passé au moins une commande :

    SELECT *
    FROM customers c
    WHERE EXISTS (
        SELECT 1
        FROM orders o
        WHERE o.customer_id = c.customer_id
    );
  5. Analyser le plan d'exécution de la requête à l'aide de EXPLAIN :

    Utilisez la commande EXPLAIN pour analyser le plan d'exécution de la requête EXISTS. Exécutez la commande SQL suivante dans l'invite MySQL :

    EXPLAIN SELECT *
    FROM customers c
    WHERE EXISTS (
        SELECT 1
        FROM orders o
        WHERE o.customer_id = c.customer_id
    );

    Comparez le plan d'exécution avec celui de la requête IN. Y a-t-il des différences dans les tables accédées, les index utilisés ou les méthodes d'accès ?

  6. Observations :

    En général, EXISTS a tendance à offrir de meilleures performances que IN lorsque la sous-requête renvoie un grand nombre de lignes. En effet, IN doit comparer les valeurs de la requête externe à toutes les valeurs renvoyées par la sous-requête, tandis que EXISTS s'arrête dès qu'il trouve une correspondance. Cependant, les performances réelles peuvent varier en fonction de la requête spécifique, des données et du système de base de données. Vous pouvez utiliser la fonction BENCHMARK() (comme montré dans le document original) pour obtenir une mesure plus précise du temps d'exécution, mais pour ce laboratoire, l'analyse de la sortie EXPLAIN fournit un aperçu suffisant des plans de requête.

  7. Nettoyage (facultatif) :

    Si vous souhaitez nettoyer la base de données et les tables, vous pouvez exécuter les commandes suivantes dans l'invite MySQL :

    DROP TABLE IF EXISTS orders;
    DROP TABLE IF EXISTS customers;
    DROP DATABASE IF EXISTS labdb;

    Lorsque vous avez terminé toutes les étapes, vous pouvez quitter le client MySQL en tapant :

    exit

Résumé

Dans ce laboratoire, vous avez appris à utiliser des sous-requêtes dans la clause WHERE d'une instruction SQL pour filtrer les données en fonction de conditions dérivées d'autres tables ou de la même table. Vous vous êtes exercé à vous connecter à un serveur MySQL, à créer une base de données et des tables, et à insérer des données d'exemple.

Vous avez exploré l'utilisation de l'opérateur IN avec une sous-requête pour trouver des clients en fonction de données connexes dans la table orders. Vous avez également découvert l'opérateur EXISTS comme alternative à IN et vous vous êtes exercé à l'utiliser pour vérifier l'existence de lignes connexes.

De plus, vous avez été initié aux sous-requêtes corrélées, qui font référence à des colonnes de la requête externe, et vous en avez utilisé une pour trouver des clients dont les commandes dépassent le montant moyen des commandes. Enfin, vous avez comparé les performances des sous-requêtes IN et EXISTS en analysant leurs plans d'exécution à l'aide de la commande EXPLAIN, acquérant ainsi une compréhension du traitement de ces requêtes par MySQL.