CROSS JOIN, CROSS APPLY y OUTER APPLY: combinar y evaluar filas
Estas construcciones comparten una palabra, pero resuelven necesidades distintas. CROSS JOIN genera combinaciones entre dos conjuntos. APPLY permite ejecutar una expresión tabular para cada fila de la consulta principal. Una expresión tabular es una consulta o función cuyo resultado tiene filas y columnas, como una tabla.
La diferencia importa porque una combinación completa puede crecer muy rápido, mientras que APPLY suele utilizarse para obtener un resultado correlacionado, como el último pedido de cada cliente. Correlacionado significa que la expresión interna utiliza un valor de la fila que la consulta exterior está procesando.
CROSS JOIN: todas las combinaciones
Si una tabla contiene tres categorías y otra cuatro estados posibles, CROSS JOIN produce doce combinaciones.
FROM dbo.TablaA AS a
CROSS JOIN dbo.TablaB AS b
SELECT
c.Nombre AS Categoria,
e.Estado
FROM dbo.Categoria AS c
CROSS JOIN
(
VALUES
('Pendiente'),
('Confirmado'),
('Enviado'),
('Cancelado')
) AS e (Estado)
ORDER BY
c.Nombre,
e.Estado;
No existe una condición ON: cada fila del primer conjunto se combina con cada fila del segundo. Esto puede servir para construir calendarios, matrices o combinaciones que después compararemos con datos existentes.
CROSS APPLY: conservar solo resultados
Queremos obtener el pedido más reciente de cada cliente. La consulta interna puede utilizar el identificador de la fila exterior:
FROM dbo.TablaExterior AS e
CROSS APPLY
(
ConsultaCorrelacionada
) AS r
SELECT
c.IdCliente,
c.Nombre,
u.IdPedido,
u.FechaPedido,
u.Estado
FROM dbo.Cliente AS c
CROSS APPLY
(
SELECT TOP (1)
p.IdPedido,
p.FechaPedido,
p.Estado
FROM dbo.Pedido AS p
WHERE p.IdCliente = c.IdCliente
ORDER BY
p.FechaPedido DESC,
p.IdPedido DESC
) AS u
ORDER BY c.IdCliente;
La expresión u se evalúa para cada cliente. La fila exterior es la fila actual de Cliente que alimenta esa evaluación. CROSS APPLY la conserva únicamente cuando la expresión devuelve al menos una fila. Por eso Carla, que no tiene pedidos, queda fuera.
OUTER APPLY: conservar también los vacíos
Si cambiamos una sola palabra, Carla vuelve al resultado:
FROM dbo.TablaExterior AS e
OUTER APPLY
(
ConsultaCorrelacionada
) AS r
SELECT
c.IdCliente,
c.Nombre,
u.IdPedido,
u.FechaPedido,
u.Estado
FROM dbo.Cliente AS c
OUTER APPLY
(
SELECT TOP (1)
p.IdPedido,
p.FechaPedido,
p.Estado
FROM dbo.Pedido AS p
WHERE p.IdCliente = c.IdCliente
ORDER BY
p.FechaPedido DESC,
p.IdPedido DESC
) AS u
ORDER BY c.IdCliente;
| IdCliente | Nombre | IdPedido | Estado |
|---|---|---|---|
| 1 | Ana Torres | 2 | Enviado |
| 2 | Bruno Díaz | 3 | Pendiente |
| 3 | Carla Ruiz | NULL |
NULL |
| 4 | Diego Soto | 4 | Cancelado |
| 5 | Elena Mora | 5 | Confirmado |
OUTER APPLY conserva la fila exterior aunque la expresión no encuentre resultados y completa las columnas de u con NULL, la marca que representa un valor ausente o desconocido.
Qué conviene comprobar
Antes de utilizar CROSS JOIN, podemos calcular el tamaño esperado multiplicando la cantidad de filas de ambos conjuntos. Mil filas combinadas con otras mil producen un millón de resultados antes de aplicar filtros posteriores.
En APPLY, la consulta correlacionada se evalúa conceptualmente para cada fila exterior. Un índice organiza valores para facilitar el acceso a las filas. Los índices, el filtro y el orden utilizados dentro de la expresión pueden cambiar de forma importante su costo.
Para obtener el último registro por grupo también existen soluciones con ROW_NUMBER. Ninguna forma es universalmente mejor: conviene comparar el plan de ejecución —la estrategia elegida por SQL Server— y las lecturas lógicas —las páginas de datos consultadas— con el volumen real.


