🇯🇵 日本語 | 🇺🇸 English | 🇪🇸 Español | 🇵🇹 Português | 🇹🇭 ไทย | 🇨🇳 中文

[SQL Intermedio] ¡Domina las Subconsultas! Guía Completa de SELECT dentro de SELECT

"Quiero encontrar solo a los empleados que ganan más que el salario promedio de toda la empresa..."
"¿Quién es el empleado con el salario más alto en cada departamento?"

Como creador web que trabaja con bases de datos, ¿alguna vez has pensado "me gustaría usar el resultado de una consulta como condición para otra consulta"? Este tipo de extracción de datos un poco más compleja no se puede hacer con una sola consulta. La solución elegante a este problema es lo que presentaremos hoy: la subconsulta.

En pocas palabras, una subconsulta es una "consulta dentro de otra consulta". Es una técnica en la que incrustas una sentencia SELECT dentro de otra, como si fueran muñecas Matrioska. Si logras dominar esto, podrás consolidar en una sola consulta extracciones de datos que antes requerían múltiples pasos, ¡y tus habilidades con SQL subirán de nivel drásticamente!

En este artículo, hemos preparado abundantes ejemplos de código que incluso los principiantes pueden copiar y pegar para que funcionen. Queremos que experimentes ese momento de "¡funcionó!" para que realmente sientas el poder y la conveniencia de las subconsultas.


Preparación: Nuestras Tablas de Práctica para Hoy

Para experimentar al máximo el poder de las subconsultas, esta vez prepararemos dos tablas: una tabla de "empleados (employees)" y una de "departamentos (departments)". Por favor, copia la siguiente sentencia SQL y ejecútala en tu propio entorno de base de datos.

¡Por supuesto, no pasa nada si no tienes un entorno listo para probar! Más adelante en el artículo, hemos preparado un "entorno de ejecución de SQL" donde puedes probar SQL usando solo tu navegador.


-- Eliminar las tablas si existen
DROP TABLE IF EXISTS employees;
DROP TABLE IF EXISTS departments;

-- Crear la tabla de departamentos
CREATE TABLE departments (
    id INT PRIMARY KEY,
    department_name VARCHAR(50)
);

-- Insertar datos de departamentos
INSERT INTO departments (id, department_name) VALUES
(1, '営業部'),
(2, '開発部'),
(3, '人事部');

-- Crear la tabla de empleados
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    department_id INT,
    salary INT,
    FOREIGN KEY (department_id) REFERENCES departments(id)
);

-- Insertar datos de empleados
INSERT INTO employees (id, name, department_id, salary) VALUES
(1, '山田 太郎', 1, 600000), -- Dpto. de Ventas
(2, '鈴木 花子', 2, 550000), -- Dpto. de Desarrollo
(3, '佐藤 次郎', 1, 400000), -- Dpto. de Ventas
(4, '高橋 三郎', 3, 650000), -- Dpto. de RRHH
(5, '田中 恵子', 2, 500000), -- Dpto. de Desarrollo
(6, '伊藤 四郎', 1, 700000), -- Dpto. de Ventas
(7, '渡辺 久美', 2, 750000), -- Dpto. de Desarrollo
(8, '山本 五郎', 3, 380000); -- Dpto. de RRHH
    

¿Todo listo? ¡Entonces, adentrémonos en el poderoso mundo de las subconsultas!


1. Lo Básico: Subconsultas en la Cláusula `WHERE`

La forma más básica e intuitiva de usar una subconsulta es como condición en la cláusula WHERE. Imagina que estás generando dinámicamente los criterios de filtrado para la consulta externa (la consulta principal) con el resultado de una consulta interna (la subconsulta).

Es una regla que las subconsultas siempre deben estar encerradas en () (paréntesis). SQL primero ejecuta la subconsulta dentro de los paréntesis y luego utiliza su resultado como condición para la consulta principal.

Ejemplo 1: Extraer empleados con salarios superiores al promedio

Intentemos encontrar a los "empleados que ganan más que el salario promedio de todos los empleados", como mencionamos al principio. Para lograr esto, necesitas dos pasos:

  1. Primero, calcular el "salario promedio de todos los empleados".
  2. Luego, usar ese salario promedio como condición para extraer la información de los empleados.

Con una subconsulta, puedes hacer esto en una sola consulta.


SELECT *
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
    

¿Qué te parece? Primero, se ejecuta (SELECT AVG(salary) FROM employees) para calcular el salario promedio (547500 en este ejemplo), y ese resultado se utiliza como condición para la consulta principal, como si fuera WHERE salary > 547500. ¡Has logrado brillantemente el objetivo con una sola consulta!


Ejemplo 2: Extraer empleados en departamentos específicos (con el operador IN)

Una subconsulta puede devolver no solo un valor único, sino también una lista de múltiples valores. Al combinar esa lista con el operador IN, puedes lograr condiciones increíblemente flexibles.

Por ejemplo, supongamos que quieres extraer a todos los empleados que pertenecen a los departamentos de 'Ventas' o 'RRHH'. Usemos una subconsulta para obtener una lista de IDs de departamento a partir de los nombres de los departamentos y usemos eso como condición para la consulta principal.


SELECT *
FROM employees
WHERE department_id IN (
    SELECT id FROM departments WHERE department_name IN ('営業部', '人事部')
);
    

En esta consulta, la subconsulta se ejecuta primero y devuelve una lista de IDs para '営業部' (Ventas) y '人事部' (RRHH), que es (1, 3). La consulta principal se interpreta entonces como WHERE department_id IN (1, 3) y extrae los datos de los empleados correspondientes. Esto es muy conveniente porque no necesitas modificar el SQL aunque los nombres de los departamentos cambien.


2. Uso Avanzado: Subconsultas en las Cláusulas `SELECT` y `FROM`

Las subconsultas no son solo para la cláusula WHERE. Son especialmente útiles en las cláusulas SELECT y FROM, expandiendo enormemente las posibilidades de tu análisis de datos.

Subconsulta en la cláusula SELECT (Subconsulta escalar)

Cuando usas una subconsulta en la cláusula SELECT, puedes obtener un valor relacionado para cada fila de la consulta principal y mostrarlo como una nueva columna. Sin embargo, este tipo de subconsulta siempre debe devolver un valor único (un valor escalar) de una fila y una columna.

Como ejemplo, mostremos la información de cada empleado junto con "el salario promedio del departamento al que pertenecen".


SELECT
    name,
    salary,
    (SELECT department_name FROM departments WHERE id = e.department_id) AS department_name,
    (SELECT AVG(salary) FROM employees WHERE department_id = e.department_id) AS department_avg_salary
FROM
    employees AS e;
    

Parece un poco complejo, ¿verdad? Esto se llama una subconsulta correlacionada, donde la subconsulta interna hace referencia a datos de cada fila de la consulta externa (en este caso, e.department_id) para ejecutarse. Por cada fila que procesa la consulta principal, se calculan el nombre del departamento y el salario promedio del departamento usando el ID del departamento de esa fila y se agregan como columnas al resultado.


Subconsulta en la cláusula FROM (Vista en línea)

Cuando usas una subconsulta en la cláusula FROM, puedes tratar el resultado de esa subconsulta como una "tabla virtual temporal (una vista en línea)". Esto es útil para el procesamiento en dos etapas, donde primero agregas o procesas datos con una subconsulta y luego ejecutas otra consulta sobre ese resultado.

Como ejemplo, vamos a "calcular el salario promedio por departamento, y luego mostrar solo aquellos departamentos donde el salario promedio es de 550,000 o más".


SELECT
    d.department_name,
    dept_avg.avg_salary
FROM
    (
        SELECT department_id, AVG(salary) AS avg_salary
        FROM employees
        GROUP BY department_id
    ) AS dept_avg
JOIN
    departments AS d ON dept_avg.department_id = d.id
WHERE
    dept_avg.avg_salary >= 550000;
    

En esta consulta, la subconsulta en la cláusula FROM se ejecuta primero, creando una tabla temporal llamada dept_avg que contiene el salario promedio para cada department_id. Luego, la consulta principal une esta tabla dept_avg con la tabla departments para mostrar los nombres y salarios promedio de los departamentos que cumplen la condición. Esta es una técnica muy efectiva cuando quieres usar una cláusula WHERE sobre el resultado de una operación GROUP BY.


¡Práctica! Ejecutemos SQL en tu Navegador

¡Gracias por esperar! Aquí tienes un entorno donde puedes ponerte manos a la obra y probar las subconsultas que has aprendido hasta ahora.

Copia todo el bloque de código de abajo, guárdalo como un archivo con un nombre como subquery_practice.html y ábrelo en tu navegador. ¡Prueba las diversas subconsultas de este artículo para tener una idea de cómo funcionan!


<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Patio de Pruebas de Subconsultas SQL</title>
    <script src="https://cdn.jsdelivr.net/npm/alasql@4"></script>
    <style>
        body { font-family: sans-serif; padding: 2rem; background-color: #f9f9f9; }
        .container { max-width: 800px; margin: auto; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
        h1 { color: #333; }
        textarea { width: 100%; height: 180px; font-family: monospace; font-size: 16px; padding: 10px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; margin-bottom: 1rem; }
        button { background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
        button:hover { background-color: #0056b3; }
        #result-area { margin-top: 2rem; }
        table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
        .error { color: red; font-weight: bold; }
    </style>
</head>
<body>
    <div class="container">
        <h1>¡Probemos las Subconsultas!</h1>
        <p>Introduce una sentencia SQL en el área de texto de abajo y presiona el botón "Ejecutar SQL".</p>
        <textarea id="sql-input">-- Escribe tu SQL aquí
SELECT
    d.department_name,
    dept_avg.avg_salary
FROM
    (
        SELECT department_id, AVG(salary) AS avg_salary
        FROM employees
        GROUP BY department_id
    ) AS dept_avg
JOIN
    departments AS d ON dept_avg.department_id = d.id
WHERE
    dept_avg.avg_salary >= 550000;</textarea>
        <button onclick="executeSQL()">Ejecutar SQL</button>
        <div id="result-area"></div>
    </div>

    <script>
        // Inicializar la base de datos y preparar los datos
        const db = new alasql.Database();
        db.exec(`
            CREATE TABLE departments (id INT PRIMARY KEY, department_name VARCHAR(50));
            INSERT INTO departments VALUES (1, '営業部'), (2, '開発部'), (3, '人事部');

            CREATE TABLE employees (id INT PRIMARY KEY, name VARCHAR(50), department_id INT, salary INT);
            INSERT INTO employees VALUES
            (1, '山田 太郎', 1, 600000), (2, '鈴木 花子', 2, 550000), (3, '佐藤 次郎', 1, 400000),
            (4, '高橋 三郎', 3, 650000), (5, '田中 恵子', 2, 500000), (6, '伊藤 四郎', 1, 700000),
            (7, '渡辺 久美', 2, 750000), (8, '山本 五郎', 3, 380000);
        `);

        function executeSQL() {
            const sql = document.getElementById('sql-input').value;
            const resultArea = document.getElementById('result-area');
            resultArea.innerHTML = '';

            try {
                const result = db.exec(sql);
                if (result.length > 0) {
                    resultArea.appendChild(createTable(result));
                } else {
                    resultArea.innerHTML = '<p>La consulta devolvió 0 filas.</p>';
                }
            } catch (e) {
                resultArea.innerHTML = `<p class="error">Error: ${e.message}</p>`;
            }
        }

        function createTable(data) {
            const table = document.createElement('table');
            const thead = table.createTHead();
            const tbody = table.createTBody();
            const headerRow = thead.insertRow();
            for (const key in data[0]) {
                const th = document.createElement('th');
                th.textContent = key;
                headerRow.appendChild(th);
            }
            data.forEach(rowData => {
                const row = tbody.insertRow();
                for (const key in rowData) {
                    const cell = row.insertCell();
                    cell.textContent = rowData[key];
                }
            });
            return table;
        }

        // Visualización inicial
        executeSQL();
    </script>
</body>
</html>
    

Puntos a Tener en Cuenta y Técnicas Relacionadas

Las subconsultas son extremadamente poderosas, pero hay algunas cosas que debes tener en cuenta. Conocerlas te ayudará a escribir consultas más sofisticadas.

Problemas de Rendimiento

Las subconsultas, especialmente las subconsultas correlacionadas que acabamos de presentar, pueden degradar significativamente el rendimiento a medida que aumenta la cantidad de datos, porque se ejecutan por cada fila de la consulta principal. En muchos casos, una subconsulta puede reescribirse usando un JOIN, que a menudo se ejecuta más rápido.

Valores de Retorno de la Subconsulta

Hay reglas sobre la forma de los datos que una subconsulta puede devolver.

Romper estas reglas causará un error, por lo que es importante ser siempre consciente del tipo de resultado que devolverá tu subconsulta.


Técnica Relacionada: Reescribir con `JOIN`

Desde una perspectiva de rendimiento, se recomienda reescribir muchas subconsultas utilizando un JOIN. Intentemos reescribir la consulta "extraer empleados en departamentos específicos" usando un JOIN.


SELECT
    e.*
FROM
    employees AS e
JOIN
    departments AS d ON e.department_id = d.id
WHERE
    d.department_name IN ('営業部', '人事部');
    

Esta consulta usando JOIN devuelve el mismo resultado que la que usa una subconsulta. Generalmente, el optimizador de una base de datos (la función que optimiza las consultas) puede procesar JOINs de manera más eficiente, por lo que deberías considerar usar un JOIN, especialmente cuando se trata de grandes cantidades de datos.


Resumen

¡Buen trabajo! Hoy, hemos profundizado en la poderosa técnica de SQL de las subconsultas.

Dominar las subconsultas te permite realizar en un solo paso la recuperación de datos que antes requería múltiples pasos, mejorando drásticamente la eficiencia de tu trabajo. ¡Por favor, utiliza el entorno de ejecución de SQL que proporcionamos para probar varios patrones y hacer tuyas las subconsultas!