0Pricing
SQL Interview Prep · Lección

Reescribir subconsultas correlacionadas como joins

Transforme la lógica correlacionada en joins o funciones de ventana para mejorar el rendimiento

Reescribir subconsultas correlacionadas como joins es una lección gratuita de SQL Interview Prep en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de SQL Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de SQL Interview Prep incluye 4 lecciones en total.

¿Por qué reescribir?

Las subconsultas correlacionadas son legibles, pero pueden ser lentas: la consulta interna puede ejecutarse una vez por cada fila externa. Los entrevistadores suelen pedirle que reescriba una como un JOIN o una función de ventana para mejorar el rendimiento.

El objetivo es obtener el mismo resultado con una sola pasada por los datos, en lugar de repetir las exploraciones internas.

Conocer dos o tres patrones de reescritura y saber cuándo cada uno conserva la corrección es una competencia fundamental de nivel intermedio.

Patrón 1: de EXISTS a INNER JOIN

Un EXISTS correlacionado que comprueba si hay al menos una coincidencia a menudo puede convertirse en un INNER JOIN.

Pero tenga cuidado: un JOIN puede producir filas externas duplicadas si coinciden varias filas internas. Agregue DISTINCT o use una agregación para recuperar una fila por clave externa.

-- Correlated EXISTS
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o
             WHERE o.customer_id = c.customer_id);

-- Join rewrite (DISTINCT avoids dupes from fan-out)
SELECT DISTINCT c.customer_id, c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;

El problema del fan-out

El error más común al reescribir es olvidar el fan-out. EXISTS devuelve cada cliente una sola vez, sin importar cuántos pedidos tenga. Un JOIN ingenuo devuelve una fila por pedido, lo que infla los recuentos.

Si un paso posterior ejecuta COUNT(*) o SUM(amount) sobre ese resultado de la unión sin agruparlo con cuidado, las cifras serán incorrectas.

Pregúntese siempre: ¿puede el JOIN multiplicar las filas? Si es así, use DISTINCT o un GROUP BY para volver a reducirlas.

Patrón 2: de NOT EXISTS a LEFT JOIN / IS NULL

La reescritura mediante anti-join es un patrón habitual en las entrevistas. Un NOT EXISTS correlacionado se convierte en un LEFT JOIN cuyo lado derecho es NULL.

Las filas externas sin coincidencia reciben valores NULL en el lado derecho; filtrar por ese NULL conserva exactamente las filas sin coincidencia.

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

-- LEFT JOIN / IS NULL rewrite
SELECT c.customer_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.customer_id IS NULL;

Elija una columna NOT NULL para comprobar

En la reescritura LEFT JOIN / IS NULL, compruebe una columna del lado derecho que nunca sea NULL cuando existe una coincidencia real, idealmente la clave de unión o la clave primaria.

Si comprueba una columna que admite NULL, no podrá distinguir una verdadera ausencia de coincidencia (ninguna fila) de una fila coincidente que simplemente tiene NULL en esa columna. Ese error devuelve filas incorrectas.

Usar la clave de unión (aquí o.customer_id) o o.order_id garantiza que NULL significa «ninguna fila coincidente».

Patrón 3: de un agregado escalar a JOIN + GROUP BY

Un agregado escalar correlacionado en SELECT puede convertirse en un JOIN con una subconsulta agrupada (una tabla derivada).

Calcule el agregado para cada grupo una sola vez y luego únalo a las filas de detalle. La consulta interna se ejecuta una sola vez en lugar de una vez por fila.

-- Correlated scalar aggregate
SELECT e1.name,
       (SELECT MAX(e2.salary) FROM employees e2
        WHERE e2.dept_id = e1.dept_id) AS dept_max
FROM employees e1;

-- Join + GROUP BY rewrite
SELECT e.name, m.dept_max
FROM employees e
JOIN (SELECT dept_id, MAX(salary) AS dept_max
      FROM employees GROUP BY dept_id) m
  ON m.dept_id = e.dept_id;

Patrón 4: la reescritura con una función de ventana

A menudo, la reescritura más clara es una función de ventana. MAX(salary) OVER (PARTITION BY dept_id) sustituye por completo al agregado correlacionado y no requiere un JOIN.

Calcula el valor del grupo en una sola pasada y conserva todas las filas de detalle. Esta suele ser la respuesta que más desean ver los entrevistadores en consultas analíticas.

SELECT name,
       dept_id,
       salary,
       MAX(salary) OVER (PARTITION BY dept_id) AS dept_max
FROM employees;

Reescritura para obtener los N mayores por grupo

Una subconsulta correlacionada que selecciona la fila superior de cada grupo (salary = MAX per dept) se puede reescribir fácilmente con ROW_NUMBER.

Particione por el grupo, ordene por la métrica y conserve el rango 1. Use RANK si desea conservar todas las filas superiores empatadas.

SELECT name, dept_id, salary
FROM (
    SELECT name, dept_id, salary,
           ROW_NUMBER() OVER (PARTITION BY dept_id
                              ORDER BY salary DESC) AS rn
    FROM employees
) t
WHERE rn = 1;

Cuándo NO reescribir

Reescribir no siempre mejora el resultado. Conserve la subconsulta correlacionada cuando:

  • El conjunto externo sea pequeño, de modo que el coste por fila sea insignificante.
  • La columna correlacionada esté bien indexada y el optimizador ya la convierta en un semi-join eficiente.
  • La legibilidad sea más importante que una microoptimización en código mantenido.

Los optimizadores modernos transforman con frecuencia EXISTS en un semi-join automáticamente. Diga que mediría con EXPLAIN antes de suponer que una reescritura ayuda.

Verificación de la equivalencia

Después de cualquier reescritura, confirme que devuelve las mismas filas y la misma cardinalidad que la versión original.

  • Compruebe que coincidan los recuentos de filas.
  • Compruebe que la expansión del JOIN no haya introducido duplicados.
  • Compruebe que los casos límite de NULL y de grupos vacíos sigan comportándose correctamente.

Una forma rápida de hacerlo: ejecute ambas versiones y aplíqueles EXCEPT en ambas direcciones; un resultado vacío significa que coinciden. Los entrevistadores valoran que verifique en lugar de darlo por hecho.

SELECT customer_id FROM query_a
EXCEPT
SELECT customer_id FROM query_b;
-- and the reverse; both empty => equivalent

Reescritura de IN como JOIN

Una subconsulta IN no correlacionada también suele poder reescribirse como un JOIN, pero se aplica la misma advertencia sobre el fan-out. IN elimina los duplicados de la pertenencia; un JOIN no lo hace.

Si la lista interna contiene claves duplicadas, el JOIN repetirá las filas externas. Use DISTINCT en el lado interno o en el resultado final para igualar la semántica de IN.

-- IN subquery
SELECT c.name FROM customers c
WHERE c.customer_id IN (SELECT o.customer_id FROM orders o);

-- Join rewrite, de-duplicated to match IN
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;

Comprobación rápida

Elija la reescritura con JOIN correcta para un anti-join correlacionado con NOT EXISTS.

Repaso: reescritura de subconsultas correlacionadas como JOIN

Conclusiones clave:

  • EXISTS → INNER JOIN (agregue DISTINCT para evitar duplicados por fan-out).
  • NOT EXISTS → LEFT JOIN ... WHERE key IS NULL (compruebe una columna que no admita NULL).
  • Agregado escalar correlacionado → un JOIN con una tabla derivada agrupada o, mejor aún, una función de ventana.
  • Máximo por grupo → ROW_NUMBER (o RANK para los empates).
  • Verifique la equivalencia y compruebe con EXPLAIN antes de suponer que una reescritura es más rápida.

Conocer ambas formas y la trampa del fan-out es exactamente lo que se evalúa en las entrevistas de nivel intermedio.

Preguntas frecuentes

¿La lección «Reescribir subconsultas correlacionadas como joins» es gratis?

Sí — el texto completo de «Reescribir subconsultas correlacionadas como joins» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de SQL Interview Prep, actualiza a CoddyKit PRO. El curso de SQL Interview Prep incluye 4 lecciones en total.

¿Qué aprenderé en «Reescribir subconsultas correlacionadas como joins»?

Transforme la lógica correlacionada en joins o funciones de ventana para mejorar el rendimiento Practicas SQL Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar SQL Interview Prep?

No se requiere experiencia previa. SQL Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Reescribir subconsultas correlacionadas como joins»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de SQL Interview Prep?

Sí. Cada lección de SQL Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Anatomía de una subconsulta correlacionada
  2. Agregaciones por grupo sin GROUP BY
  3. EXISTS y NOT EXISTS correlacionados
  4. Reescribir subconsultas correlacionadas como joins
← Volver a SQL Interview Prep