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

Tres categorías combinadas con cuatro estados mediante CROSS JOIN

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.

Evaluación por cliente y filas conservadas por CROSS APPLY

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.

Evaluación por cliente y fila vacía conservada por OUTER APPLY

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.

Relacionado con