Subconsultas y operaciones anidadas en MySQL

MySQLMySQLBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, explorarás el poder de las subconsultas y operaciones anidadas de MySQL. El enfoque principal es el uso de subconsultas dentro de la cláusula WHERE para filtrar datos basados en condiciones derivadas de otras tablas o de la misma tabla.

Aprenderás cómo conectarte a un servidor MySQL, crear una base de datos y tablas (clientes y pedidos), y luego construir consultas SQL que utilicen subconsultas para identificar a los clientes que han realizado pedidos con un monto total superior a un valor específico. El laboratorio también cubre el uso de EXISTS con una subconsulta, la prueba de subconsultas correlacionadas y la comparación del rendimiento de las subconsultas.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL mysql(("MySQL")) -.-> mysql/BasicKeywordsandStatementsGroup(["Basic Keywords and Statements"]) mysql(("MySQL")) -.-> mysql/AdvancedFeaturesGroup(["Advanced Features"]) mysql/BasicKeywordsandStatementsGroup -.-> mysql/use_database("Database Selection") mysql/BasicKeywordsandStatementsGroup -.-> mysql/create_database("Database Creation") mysql/BasicKeywordsandStatementsGroup -.-> mysql/drop_database("Database Deletion") mysql/BasicKeywordsandStatementsGroup -.-> mysql/create_table("Table Creation") mysql/BasicKeywordsandStatementsGroup -.-> mysql/drop_table("Table Removal") mysql/BasicKeywordsandStatementsGroup -.-> mysql/select("Data Retrieval") mysql/BasicKeywordsandStatementsGroup -.-> mysql/insert("Data Insertion") mysql/AdvancedFeaturesGroup -.-> mysql/stored_procedures("Procedure Management") subgraph Lab Skills mysql/use_database -.-> lab-550916{{"Subconsultas y operaciones anidadas en MySQL"}} mysql/create_database -.-> lab-550916{{"Subconsultas y operaciones anidadas en MySQL"}} mysql/drop_database -.-> lab-550916{{"Subconsultas y operaciones anidadas en MySQL"}} mysql/create_table -.-> lab-550916{{"Subconsultas y operaciones anidadas en MySQL"}} mysql/drop_table -.-> lab-550916{{"Subconsultas y operaciones anidadas en MySQL"}} mysql/select -.-> lab-550916{{"Subconsultas y operaciones anidadas en MySQL"}} mysql/insert -.-> lab-550916{{"Subconsultas y operaciones anidadas en MySQL"}} mysql/stored_procedures -.-> lab-550916{{"Subconsultas y operaciones anidadas en MySQL"}} end

Escribir una subconsulta en una cláusula WHERE

En este paso, aprenderás cómo usar una subconsulta dentro de la cláusula WHERE de una declaración SQL. Una subconsulta es una consulta anidada dentro de otra consulta. Es una herramienta poderosa para recuperar datos basados en condiciones derivadas de otras tablas o de la misma tabla.

Comprender las subconsultas

Una subconsulta (o consulta interna) es una consulta SQL anidada dentro de una consulta más grande. La subconsulta se ejecuta primero, y su resultado se utiliza en la consulta externa. Las subconsultas pueden aparecer en las cláusulas WHERE, SELECT, FROM y HAVING.

En la cláusula WHERE, una subconsulta se utiliza típicamente para filtrar los resultados de la consulta externa basados en una condición. La subconsulta devuelve un valor único o un conjunto de valores que la consulta externa utiliza para la comparación.

Escenario

Imagina que tienes dos tablas: customers y orders. La tabla customers contiene información de los clientes (por ejemplo, customer_id, name, city), y la tabla orders contiene información de los pedidos (por ejemplo, order_id, customer_id, order_date, total_amount).

Quieres encontrar a todos los clientes que hayan realizado al menos un pedido con un monto total mayor a $100.

Pasos

  1. Conectarse al servidor MySQL:

    Primero, conéctate a tu servidor MySQL utilizando el cliente mysql. Necesitarás el nombre de usuario y la contraseña de tu servidor MySQL. Para este laboratorio, supongamos que puedes conectarte como el usuario root sin contraseña.

    Abre tu terminal y ejecuta el siguiente comando:

    mysql -u root

    Deberías ver el indicador de MySQL: mysql>.

  2. Crear la base de datos y las tablas:

    Si aún no tienes una base de datos y tablas, créalas ahora. Vamos a crear una base de datos llamada labdb y las tablas customers y orders.

    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,
        customer_id INT,
        order_date DATE,
        total_amount DECIMAL(10, 2),
        FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
    );
  3. Insertar datos de muestra:

    Inserta algunos datos de muestra en las tablas.

    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 (order_id, customer_id, order_date, total_amount) VALUES
    (101, 1, '2023-01-15', 120.00),
    (102, 2, '2023-02-20', 80.00),
    (103, 1, '2023-03-10', 150.00),
    (104, 3, '2023-04-05', 200.00),
    (105, 2, '2023-05-12', 110.00),
    (106, 4, '2023-06-18', 90.00);
  4. Escribir la subconsulta en la cláusula WHERE:

    Ahora, escribe la consulta para encontrar a los clientes que hayan realizado pedidos con un monto total mayor a $100.

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

    Explicación:

    • La subconsulta (SELECT customer_id FROM orders WHERE total_amount > 100) selecciona el customer_id de la tabla orders donde el total_amount es mayor a 100.
    • La consulta externa SELECT * FROM customers WHERE customer_id IN (...) selecciona todas las columnas de la tabla customers donde el customer_id está en el conjunto de customer_id devueltos por la subconsulta.
  5. Ejecutar la consulta:

    Ejecuta la consulta en tu cliente MySQL. Deberías ver la siguiente salida:

    +-------------+-------------+-----------+
    | 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)

    Esto muestra que Alice Smith, Bob Johnson y Charlie Brown han realizado pedidos con un monto total mayor a $100.

  6. Alternativa utilizando EXISTS:

    También puedes obtener el mismo resultado utilizando el operador EXISTS. Este enfoque puede ser a veces más eficiente, especialmente con conjuntos de datos más grandes.

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

    Esta consulta comprueba si existe al menos un pedido para cada cliente con un monto total mayor a 100.

  7. Ejecutar la consulta con EXISTS:

    Ejecuta la consulta en tu cliente MySQL. Deberías ver la misma salida que antes:

    +-------------+-------------+-----------+
    | 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)
  8. Limpiar (opcional):

    Si quieres limpiar la base de datos y las tablas, puedes ejecutar los siguientes comandos:

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

    Recuerda salir del cliente MySQL escribiendo exit y presionando Enter.

Usar EXISTS con una subconsulta

En este paso, aprenderás cómo usar el operador EXISTS con una subconsulta en MySQL. El operador EXISTS se utiliza para comprobar la existencia de filas en una subconsulta. Devuelve TRUE si la subconsulta devuelve alguna fila, y FALSE en caso contrario.

Comprender EXISTS

El operador EXISTS se utiliza a menudo en la cláusula WHERE de una declaración SQL para filtrar resultados basados en la existencia de datos relacionados en otra tabla. Es una poderosa alternativa al uso de operaciones IN o JOIN, y a veces puede ser más eficiente, especialmente cuando se trabaja con grandes conjuntos de datos.

A diferencia de IN, EXISTS no recupera realmente los datos de la subconsulta. Simplemente comprueba si se devuelven alguna filas. Esto puede hacer que sea más rápido que IN cuando solo necesitas saber si existe una coincidencia, no los valores reales.

Escenario

Continuando con las tablas customers y orders del paso anterior, encontremos a todos los clientes que hayan realizado al menos un pedido.

Requisitos previos

Asegúrate de haber completado el paso anterior ("Escribir una subconsulta en una cláusula WHERE") y tener la base de datos labdb, la tabla customers y la tabla orders llenas de datos. Si no es así, por favor sigue las instrucciones del paso anterior para crear la base de datos y las tablas e insertar los datos de muestra.

Pasos

  1. Conectarse al servidor MySQL:

    Conéctate a tu servidor MySQL utilizando el cliente mysql.

    mysql -u root
  2. Seleccionar la base de datos:

    Selecciona la base de datos labdb.

    USE labdb;
  3. Escribir la consulta utilizando EXISTS:

    Escribe la consulta para encontrar a los clientes que hayan realizado al menos un pedido.

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

    Explicación:

    • La consulta externa SELECT * FROM customers c selecciona todas las columnas de la tabla customers, con alias c.
    • La cláusula WHERE EXISTS (...) comprueba si la subconsulta devuelve alguna fila.
    • La subconsulta SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id selecciona el valor 1 (podría ser cualquier valor constante) de la tabla orders, con alias o, donde el customer_id en la tabla orders coincide con el customer_id en la tabla customers.
    • El operador EXISTS devuelve TRUE si la subconsulta devuelve al menos una fila, lo que indica que el cliente ha realizado al menos un pedido.
  4. Ejecutar la consulta:

    Ejecuta la consulta en tu cliente MySQL. Deberías ver la siguiente salida:

    +-------------+-------------+-----------+
    | 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)

    Esto muestra que los cuatro clientes han realizado al menos un pedido.

  5. Modificar la consulta (opcional):

    Modifiquemos la consulta para encontrar a los clientes que no han realizado ningún pedido. Puedes hacer esto utilizando NOT EXISTS.

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

    Dado que todos los clientes en nuestros datos de muestra han realizado pedidos, esta consulta debería devolver un conjunto de resultados vacío.

  6. Ejecutar la consulta modificada:

    Ejecuta la consulta modificada en tu cliente MySQL. Deberías ver la siguiente salida:

    Empty set (0.00 sec)
  7. Limpiar (opcional):

    Si quieres limpiar la base de datos y las tablas, puedes ejecutar los siguientes comandos:

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

    Recuerda salir del cliente MySQL escribiendo exit y presionando Enter.

Probar una subconsulta correlacionada

En este paso, aprenderás sobre las subconsultas correlacionadas en MySQL. Una subconsulta correlacionada es una subconsulta que hace referencia a una columna de la consulta externa. Esto significa que la subconsulta se ejecuta una vez para cada fila de la consulta externa.

Comprender las subconsultas correlacionadas

A diferencia de una subconsulta simple, que se ejecuta solo una vez, una subconsulta correlacionada depende de la consulta externa para obtener sus valores. La subconsulta utiliza los valores de la fila actual de la consulta externa para determinar su resultado. Esto hace que las subconsultas correlacionadas sean más poderosas para ciertos tipos de consultas, pero también potencialmente menos eficientes que las subconsultas simples, especialmente para conjuntos de datos grandes.

Escenario

Continuando con las tablas customers y orders, encontremos a todos los clientes que hayan realizado un pedido con un monto mayor que el monto promedio de todos los pedidos.

Requisitos previos

Asegúrate de haber completado los pasos anteriores y tener la base de datos labdb, la tabla customers y la tabla orders llenas de datos. Si no es así, por favor sigue las instrucciones de los pasos anteriores para crear la base de datos y las tablas e insertar los datos de muestra.

Pasos

  1. Conectarse al servidor MySQL:

    Conéctate a tu servidor MySQL utilizando el cliente mysql.

    mysql -u root
  2. Seleccionar la base de datos:

    Selecciona la base de datos labdb.

    USE labdb;
  3. Escribir la subconsulta correlacionada:

    Escribe la consulta para encontrar a los clientes que hayan realizado un pedido con un monto mayor que el monto promedio de los pedidos.

    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)
    );

    Explicación:

    • La consulta externa SELECT c.customer_id, c.name FROM customers c selecciona el customer_id y el name de la tabla customers, con alias c.
    • La cláusula WHERE EXISTS (...) comprueba si la subconsulta devuelve alguna fila.
    • La subconsulta correlacionada SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.total_amount > (SELECT AVG(total_amount) FROM orders) selecciona el valor 1 de la tabla orders, con alias o, donde:
      • o.customer_id = c.customer_id: Esta es la correlación. La subconsulta hace referencia al customer_id de la tabla customers de la consulta externa.
      • o.total_amount > (SELECT AVG(total_amount) FROM orders): Esta condición comprueba si el monto del pedido es mayor que el monto promedio de todos los pedidos. La subconsulta AVG(total_amount) es una subconsulta no correlacionada que se ejecuta solo una vez para obtener el monto promedio de los pedidos.
  4. Ejecutar la consulta:

    Ejecuta la consulta en tu cliente MySQL. Primero, determinemos el monto promedio de los pedidos.

    SELECT AVG(total_amount) FROM orders;

    La salida debería ser algo como:

    +---------------------+
    | AVG(total_amount)   |
    +---------------------+
    | 125.0000            |
    +---------------------+
    1 row in set (0.00 sec)

    Entonces, el monto promedio de los pedidos es 125. Ahora, ejecuta la subconsulta correlacionada:

    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)
    );

    Deberías ver la siguiente salida:

    +-------------+-------------+
    | customer_id | name        |
    +-------------+-------------+
    |           1 | Alice Smith |
    |           3 | Charlie Brown |
    +-------------+-------------+
    2 rows in set (0.00 sec)

    Esto muestra que Alice Smith y Charlie Brown han realizado pedidos con un monto mayor que el monto promedio de los pedidos.

  5. Otro ejemplo: Encontrar el monto máximo de pedido para cada cliente

    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;

    Esta consulta recupera el ID, el nombre y el monto máximo de pedido de cada cliente. La subconsulta correlacionada calcula el monto máximo de pedido para cada cliente individualmente.

  6. Ejecutar la consulta:

    Ejecuta la consulta en tu cliente MySQL. Deberías ver la siguiente salida:

    +-------------+-------------+-----------------------+
    | 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)
  7. Limpiar (opcional):

    Si quieres limpiar la base de datos y las tablas, puedes ejecutar los siguientes comandos:

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

    Recuerda salir del cliente MySQL escribiendo exit y presionando Enter.

Comparar el rendimiento de las subconsultas

En este paso, aprenderás cómo comparar el rendimiento de diferentes tipos de subconsultas en MySQL. Comprender las características de rendimiento de las subconsultas es crucial para escribir consultas SQL eficientes, especialmente cuando se trabaja con grandes conjuntos de datos.

Comprender las consideraciones de rendimiento

El rendimiento de una subconsulta puede verse afectado por varios factores, entre ellos:

  • Tamaño de los datos: El tamaño de las tablas involucradas en la consulta.
  • Tipo de subconsulta: Si la subconsulta es correlacionada o no correlacionada.
  • Indexación: La presencia y eficacia de los índices en las tablas.
  • Versión de MySQL: La versión específica de MySQL que se está utilizando, ya que las técnicas de optimización de consultas pueden variar.

Escenario

Continuando con las tablas customers y orders, comparemos el rendimiento de una subconsulta que utiliza IN frente a una subconsulta que utiliza EXISTS para encontrar a todos los clientes que hayan realizado al menos un pedido.

Requisitos previos

Asegúrate de haber completado los pasos anteriores y tener la base de datos labdb, la tabla customers y la tabla orders llenas de datos. Para que la comparación de rendimiento sea más significativa, agregaremos más datos a la tabla orders.

Pasos

  1. Conectarse al servidor MySQL:

    Conéctate a tu servidor MySQL utilizando el cliente mysql.

    mysql -u root
  2. Seleccionar la base de datos:

    Selecciona la base de datos labdb.

    USE labdb;
  3. Agregar más datos a la tabla orders:

    Para que la comparación de rendimiento sea más realista, agreguemos una cantidad significativa de datos a la tabla orders. Insertaremos 1000 pedidos para cada cliente. Crea un archivo llamado ~/project/insert_orders.sql con el siguiente contenido:

    USE labdb;
    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;

    Explicación:

    • Este script SQL crea un procedimiento almacenado llamado insert_many_orders.
    • El procedimiento inserta 1000 pedidos para cada uno de los cuatro clientes en la tabla orders.
    • Después de insertar los datos, se elimina el procedimiento.
  4. Ejecutar el script SQL:

    Ejecuta el script SQL utilizando el siguiente comando:

    mysql -u root < ~/project/insert_orders.sql
  5. Consulta utilizando IN:

    Ejecuta la siguiente consulta utilizando IN para encontrar a todos los clientes que hayan realizado al menos un pedido.

    SELECT *
    FROM customers
    WHERE customer_id IN (SELECT customer_id FROM orders);
  6. Analizar el plan de ejecución de la consulta utilizando EXPLAIN:

    Antes de ejecutar la consulta, utiliza el comando EXPLAIN para analizar el plan de ejecución de la consulta. Esto te dará información sobre cómo MySQL planea ejecutar la consulta e identificará posibles cuellos de botella de rendimiento.

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

    La salida de EXPLAIN mostrará las tablas que se están accediendo, los índices que se están utilizando (si los hay) y el orden en el que se realizan las operaciones. Presta atención a la columna type, que indica el tipo de join o método de acceso utilizado.

  7. Ejecutar la consulta y medir el tiempo de ejecución:

    Para medir el tiempo de ejecución, utiliza la función BENCHMARK(). Esta función ejecuta una expresión un número especificado de veces y devuelve el tiempo que tarda en ejecutarse. Dado que queremos medir el tiempo de una sola ejecución, utilizaremos BENCHMARK(1, ...)

    SELECT BENCHMARK(1, (SELECT *
    FROM customers
    WHERE customer_id IN (SELECT customer_id FROM orders)));

    La salida será una sola fila con el resultado del benchmark. Anota el tiempo que tarda en ejecutarse.

  8. Consulta utilizando EXISTS:

    Ejecuta la siguiente consulta utilizando EXISTS para encontrar a todos los clientes que hayan realizado al menos un pedido.

    SELECT *
    FROM customers c
    WHERE EXISTS (
        SELECT 1
        FROM orders o
        WHERE o.customer_id = c.customer_id
    );
  9. Analizar el plan de ejecución de la consulta utilizando EXPLAIN:

    Utiliza el comando EXPLAIN para analizar el plan de ejecución de la consulta para la consulta EXISTS.

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

    Compara el plan de ejecución con el de la consulta IN. ¿Hay alguna diferencia en las tablas accedidas, los índices utilizados o los métodos de acceso?

  10. Ejecutar la consulta y medir el tiempo de ejecución:

    Mide el tiempo de ejecución de la consulta EXISTS utilizando la función BENCHMARK().

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

    Compara el tiempo de ejecución con el de la consulta IN. ¿Qué consulta fue más rápida?

  11. Observaciones:

    En general, EXISTS tiende a tener un mejor rendimiento que IN cuando la subconsulta devuelve un gran número de filas. Esto se debe a que IN necesita comparar los valores de la consulta externa con todos los valores devueltos por la subconsulta, mientras que EXISTS se detiene tan pronto como encuentra una coincidencia. Sin embargo, el rendimiento real puede variar dependiendo de la consulta específica, los datos y el sistema de base de datos.

  12. Limpiar (opcional):

    Si quieres limpiar la base de datos y las tablas, puedes ejecutar los siguientes comandos:

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

    Recuerda salir del cliente MySQL escribiendo exit y presionando Enter.

Resumen

En este laboratorio, comenzaste aprendiendo cómo utilizar subconsultas dentro de la cláusula WHERE de una declaración SQL para filtrar resultados basados en condiciones derivadas de otras tablas o de la misma tabla. El laboratorio enfatiza que la subconsulta se ejecuta primero y que su resultado se utiliza por la consulta externa para la comparación.

Los pasos iniciales implican conectarse a un servidor MySQL utilizando el cliente mysql, crear una base de datos llamada labdb y configurar dos tablas: customers y orders. Esta configuración proporciona la base para practicar subconsultas en los pasos posteriores.