JOIN ON vs WHERE: por qué la ubicación de la condición importa

Written by

in

Concepto fundamental

En SQL, una condición puede colocarse en la cláusula ON del JOIN, o en la cláusula WHERE. Aunque en muchos casos parecen producir el mismo resultado, no siempre son equivalentes, especialmente cuando se usan LEFT JOIN, RIGHT JOIN o FULL OUTER JOIN.

Orden lógico de ejecución

De forma simplificada, SQL procesa una consulta en este orden:

FROM
JOIN
ON
WHERE
GROUP BY
HAVING
SELECT
ORDER BY

Esto significa que primero se realizan los JOIN, luego se aplican las condiciones del ON, y después se ejecuta el WHERE sobre el resultado ya obtenido.

Diferencia conceptual

Condición en ON

La condición forma parte del criterio de unión entre las tablas:

SELECT *
FROM tablaA a
JOIN tablaB b
    ON a.id = b.id
   AND b.estado = 'ACTIVO';

Interpretación: une ambas tablas solamente cuando el ID coincida y el registro de B esté ACTIVO.

Condición en WHERE

La unión se realiza primero y luego se filtran los resultados:

SELECT *
FROM tablaA a
JOIN tablaB b
    ON a.id = b.id
WHERE b.estado = 'ACTIVO';

Interpretación: une ambas tablas por ID y después elimina todas las filas cuyo estado no sea ACTIVO.

Caso 1: INNER JOIN

Tablas de ejemplo:

clientes
idnombre
1Ana
2Luis
3Pedro
pedidos
idcliente_idestado
101ACTIVO
111INACTIVO
122INACTIVO

Opción A: condición en ON

SELECT *
FROM clientes c
INNER JOIN pedidos p
    ON c.id = p.cliente_id
   AND p.estado = 'ACTIVO';
clientepedidoestado
Ana10ACTIVO

Opción B: condición en WHERE

SELECT *
FROM clientes c
INNER JOIN pedidos p
    ON c.id = p.cliente_id
WHERE p.estado = 'ACTIVO';
clientepedidoestado
Ana10ACTIVO

Conclusión para INNER JOIN

En la mayoría de los casos, INNER JOIN ... ON condicion y INNER JOIN ... WHERE condicion producen el mismo resultado. Por eso muchas personas creen erróneamente que siempre es indiferente dónde colocar la condición.

Caso 2: LEFT JOIN

Aquí es donde aparecen los problemas.

LEFT JOIN sin filtros

SELECT *
FROM clientes c
LEFT JOIN pedidos p
    ON c.id = p.cliente_id;
clientepedidoestado
Ana10ACTIVO
Ana11INACTIVO
Luis12INACTIVO
PedroNULLNULL

Pedro aparece aunque no tenga pedidos — ese es justamente el propósito del LEFT JOIN.

Condición en ON (correcto)

Supongamos que queremos mostrar todos los clientes y únicamente sus pedidos activos:

SELECT *
FROM clientes c
LEFT JOIN pedidos p
    ON c.id = p.cliente_id
   AND p.estado = 'ACTIVO';

Ana tiene un pedido ACTIVO, se muestra. Luis tiene pedidos pero ninguno ACTIVO, se devuelve NULL. Pedro no tiene pedidos, se devuelve NULL. Todos los clientes siguen apareciendo:

clientepedidoestado
Ana10ACTIVO
LuisNULLNULL
PedroNULLNULL

Condición en WHERE (error típico)

SELECT *
FROM clientes c
LEFT JOIN pedidos p
    ON c.id = p.cliente_id
WHERE p.estado = 'ACTIVO';

Primero SQL realiza el LEFT JOIN completo (las 4 filas de arriba), y luego el WHERE elimina todo lo que no sea ACTIVO, incluyendo los NULL. Resultado final:

clientepedidoestado
Ana10ACTIVO

Problema: desaparecieron Luis y Pedro. El LEFT JOIN terminó comportándose como un INNER JOIN.

La regla más importante

Cuando uses LEFT JOIN, pregúntate: ¿quiero conservar los registros que no tengan coincidencia? Si la respuesta es sí, las condiciones sobre la tabla derecha generalmente deben ir en el ON.

Ejemplo típico en seguros

Supongamos estas tablas:

póliza
P001
P002
P003
pólizaestado
P001PAGADO
P002PENDIENTE

Objetivo: mostrar todas las pólizas y solamente los pagos pendientes.

Correcto

SELECT *
FROM polizas p
LEFT JOIN pagos pa
    ON p.poliza = pa.poliza
   AND pa.estado = 'PENDIENTE';
pólizaestado
P001NULL
P002PENDIENTE
P003NULL

Incorrecto

SELECT *
FROM polizas p
LEFT JOIN pagos pa
    ON p.poliza = pa.poliza
WHERE pa.estado = 'PENDIENTE';
pólizaestado
P002PENDIENTE

Se perdieron P001 y P003.

Regla práctica para el trabajo diario

INNER JOIN

Generalmente da igual si la condición va en el ON o en el WHERE.

LEFT JOIN

Si la condición afecta a la tabla de la derecha:

  • Normalmente correcto: LEFT JOIN tablaB ON ... AND condicion
  • Sospechoso: LEFT JOIN tablaB ON ... WHERE tablaB.columna = valor — muchas veces esto destruye el LEFT JOIN.

Truco mental rápido

Si ves un LEFT JOIN seguido de un WHERE que filtra sobre la tabla derecha, detente y pregúntate: ¿quiero eliminar los NULL? Si la respuesta es no, la condición probablemente debe estar en el ON. Si es sí, el WHERE está bien.

Resumen final

Ubicación de la condiciónFunción
ONDefine cómo se unen las tablas
WHEREFiltra el resultado después del JOIN
INNER JOINGeneralmente produce el mismo resultado en ambos casos
LEFT JOINPuede cambiar completamente el resultado
ON + LEFT JOINConserva registros sin coincidencia
WHERE + LEFT JOINPuede convertirlo en un INNER JOIN

Regla de oro: si utilizas LEFT JOIN y quieres conservar los registros de la tabla izquierda aunque no exista coincidencia en la tabla derecha, coloca las condiciones de la tabla derecha dentro del ON y no en el WHERE.