Patrón SQL · ausencia de coincidencias

Anti-join en SQL: encontrar filas sin coincidencia

Un anti-join responde preguntas como «¿qué clientes no tienen pedidos?» o «¿qué productos nunca se vendieron?». La forma más directa suele ser NOT EXISTS; también puedes expresarlo con LEFT JOIN ... IS NULL.

Lectura: 6–8 minEjemplos reproduciblesPráctica con pistas
Contenido de esta guía

Respuesta rápida

Un anti-join conserva las filas de una tabla para las que no existe una coincidencia en otra. SQL no necesita una palabra clave ANTI JOIN: normalmente se escribe con una subconsulta correlacionada NOT EXISTS o con un LEFT JOIN seguido de una comprobación IS NULL sobre una columna no nula de la tabla derecha.

Opción recomendada: NOT EXISTS

Si la pregunta de negocio es «no existe un pedido de este cliente», NOT EXISTS expresa exactamente esa condición. La subconsulta se correlaciona con la fila exterior mediante la clave relacionada.

Clientes sin pedidos
WITH clientes(id, nombre) AS (
  VALUES (1, 'Ana'), (2, 'Bruno'), (3, 'Celia'), (4, 'Diego')
),
pedidos(id, cliente_id) AS (
  VALUES (101, 1), (102, 1), (103, 3)
)
SELECT c.id, c.nombre
FROM clientes AS c
WHERE NOT EXISTS (
  SELECT 1
  FROM pedidos AS p
  WHERE p.cliente_id = c.id
)
ORDER BY c.id;
Resultado comprobado en SQLite 3.46.1
idnombre
2Bruno
4Diego

La lista del SELECT interno no es lo importante: EXISTS y NOT EXISTS se fijan en si la subconsulta devuelve alguna fila. Por convención suele verse SELECT 1.

Alternativa: LEFT JOIN + IS NULL

También puedes conservar todos los clientes, unir sus pedidos y quedarte con las filas donde no apareció una coincidencia derecha.

Mismo anti-join con LEFT JOIN
WITH clientes(id, nombre) AS (
  VALUES (1, 'Ana'), (2, 'Bruno'), (3, 'Celia'), (4, 'Diego')
),
pedidos(id, cliente_id) AS (
  VALUES (101, 1), (102, 1), (103, 3)
)
SELECT c.id, c.nombre
FROM clientes AS c
LEFT JOIN pedidos AS p
  ON p.cliente_id = c.id
WHERE p.id IS NULL
ORDER BY c.id;

Usa en el filtro una columna de la tabla derecha que sepas que no puede ser NULL en una fila real, como una clave primaria. Si filtras una columna derecha que sí admite nulos, podrías confundir «hubo coincidencia pero el dato es nulo» con «no hubo coincidencia».

¿Por qué no empezar por NOT IN?

NOT IN puede ser válido, pero tiene una trampa importante cuando la lista o subconsulta contiene NULL: la lógica de tres valores puede hacer que la exclusión no devuelva las filas que esperas. Para una pregunta de ausencia relacional, NOT EXISTS suele comunicar mejor la intención y evita depender de que el conjunto interior esté libre de nulos.

Regla práctica: «no existe una fila relacionada» → piensa primero en NOT EXISTS. «el valor no pertenece a una lista conocida y sin NULL» → NOT IN puede ser suficiente.

Rendimiento: escribe la lógica correcta y luego mide

Los optimizadores modernos pueden transformar estas formas en estrategias de semi-join o anti-join internas. No asumas que una sintaxis siempre será más rápida. Asegura índices razonables en las columnas de relación, revisa el plan con las herramientas del motor y mide con tus datos reales.

Práctica: productos sin ventas

Intermedia · joins y subconsultas

Encuentra productos sin ninguna venta

Con las CTE siguientes, devuelve id y nombre de los productos que no aparecen en ventas. Usa NOT EXISTS.

Qué debes recordar

  • Un anti-join busca filas sin correspondencia.
  • NOT EXISTS expresa directamente la ausencia de una fila relacionada.
  • LEFT JOIN ... IS NULL es equivalente si filtras una columna derecha que no pueda ser nula en una coincidencia real.
  • NOT IN exige especial cuidado con NULL.
  • El rendimiento depende del motor, los índices, la cardinalidad y el plan real.

Conceptos relacionados

Fuentes técnicas consultadas