CROSS JOIN
El CROSS JOIN es fundamentalmente diferente a los otros tipos de JOIN. No necesita una condición de unión porque su propósito es generar el producto cartesiano de dos tablas: combina cada fila de la primera tabla con cada fila de la segunda. Si la primera tabla tiene 10 filas y la segunda tiene 5, el resultado tendrá 50 filas (10 × 5).
A primera vista puede parecer inútil o incluso peligroso (puede generar resultados enormes), pero tiene casos de uso muy específicos donde es exactamente la herramienta correcta.
Sintaxis
-- Sintaxis explícita
SELECT columnas
FROM tabla1
CROSS JOIN tabla2;
-- Sintaxis implícita (equivalente)
SELECT columnas
FROM tabla1, tabla2;Ambas formas producen el mismo resultado. No hay cláusula ON porque no existe una condición de relación: se generan todas las combinaciones posibles. Si añades un WHERE, el resultado se filtra después de generar el producto cartesiano.
Producto cartesiano
Para visualizar lo que hace un CROSS JOIN, combinemos los 3 vendedores con los 5 estados posibles de un pedido:
SELECT
e.nombre AS empleado,
p.estado
FROM empleados e
CROSS JOIN (SELECT DISTINCT estado FROM pedidos) AS p
WHERE e.puesto LIKE '%Vendedor%' OR e.puesto LIKE '%Vendedora%'
ORDER BY e.nombre, p.estado;| empleado | estado |
|---|---|
| Daniel | cancelado |
| Daniel | entregado |
| Daniel | enviado |
| Daniel | pendiente |
| Daniel | procesando |
| Natalia | cancelado |
| Natalia | entregado |
| Natalia | enviado |
| Natalia | pendiente |
| Natalia | procesando |
| Patricia | cancelado |
| Patricia | entregado |
| Patricia | enviado |
| Patricia | pendiente |
| Patricia | procesando |
| Raúl | cancelado |
| Raúl | entregado |
| Raúl | enviado |
| Raúl | pendiente |
| Raúl | procesando |
Cada empleado de ventas se combina con cada estado posible, generando 4 × 5 = 20 filas. Todavía no es útil por sí solo, pero es el primer paso para construir informes completos donde necesitas que aparezcan todas las combinaciones, incluso las que no tienen datos.
Generar una matriz completa con LEFT JOIN
El caso de uso más práctico del CROSS JOIN es combinarlo con un LEFT JOIN para crear una matriz que incluya los ceros. Si quisieras ver cuántos pedidos ha gestionado cada vendedor en cada estado, incluyendo las combinaciones que no han ocurrido:
SELECT
e.nombre AS empleado,
estados.estado,
COUNT(p.id) AS pedidos
FROM empleados e
CROSS JOIN (SELECT DISTINCT estado FROM pedidos) AS estados
LEFT JOIN pedidos p ON e.id = p.empleado_id
AND p.estado = estados.estado
WHERE e.id IN (4, 5, 6)
GROUP BY e.nombre, estados.estado
ORDER BY e.nombre, estados.estado;| empleado | estado | pedidos |
|---|---|---|
| Daniel | cancelado | 1 |
| Daniel | entregado | 3 |
| Daniel | enviado | 1 |
| Daniel | pendiente | 2 |
| Daniel | procesando | 1 |
| Natalia | cancelado | 1 |
| Natalia | entregado | 3 |
| Natalia | enviado | 2 |
| Natalia | pendiente | 3 |
| Natalia | procesando | 1 |
| Patricia | cancelado | 1 |
| Patricia | entregado | 1 |
| Patricia | enviado | 1 |
| Patricia | pendiente | 2 |
| Patricia | procesando | 2 |
El CROSS JOIN genera todas las combinaciones empleado-estado (15 filas para 3 empleados × 5 estados). El LEFT JOIN con pedidos rellena los conteos reales. Si algún empleado no tuviera pedidos en algún estado, esa combinación aparecería con 0 en lugar de estar ausente, lo que es exactamente lo que necesitas para un informe tipo tabla dinámica.
Generar combinaciones de productos
Otro uso práctico: generar pares de productos que podrían recomendarse juntos. Si quisiéramos buscar qué pares de productos se han comprado en el mismo pedido:
SELECT DISTINCT
p1.nombre AS producto_1,
p2.nombre AS producto_2
FROM detalle_pedidos dp1
INNER JOIN detalle_pedidos dp2 ON dp1.pedido_id = dp2.pedido_id
AND dp1.producto_id < dp2.producto_id
INNER JOIN productos p1 ON dp1.producto_id = p1.id
INNER JOIN productos p2 ON dp2.producto_id = p2.id
ORDER BY p1.nombre
LIMIT 8;| producto_1 | producto_2 |
|---|---|
| 1984 | Eloquent JavaScript |
| 1984 | Cable USB-C a Lightning |
| Banda elástica set x5 | Zapatillas running pro |
| Cable USB-C a Lightning | Camiseta algodón básica |
| Cable USB-C a Lightning | Camiseta técnica running |
| Cable USB-C a Lightning | Clean Code |
| Cable USB-C a Lightning | Eloquent JavaScript |
| Cable USB-C a Lightning | Robot de cocina |
Aunque esta consulta usa INNER JOIN en lugar de CROSS JOIN, el concepto subyacente es el mismo: generar combinaciones. La condición dp1.producto_id < dp2.producto_id evita duplicados (no queremos ver tanto "iPhone + Funda" como "Funda + iPhone").
Tamaño del resultado
El peligro principal del CROSS JOIN es el tamaño del resultado. Si una tabla tiene 1.000 filas y otra tiene 10.000, el producto cartesiano genera 10.000.000 filas. Con tablas grandes, esto puede consumir toda la memoria disponible o bloquear el servidor.
-- Para estimar el tamaño antes de ejecutar
SELECT
(SELECT COUNT(*) FROM productos) AS filas_productos,
(SELECT COUNT(*) FROM categorias) AS filas_categorias,
(SELECT COUNT(*) FROM productos) *
(SELECT COUNT(*) FROM categorias) AS filas_cross_join;| filas_productos | filas_categorias | filas_cross_join |
|---|---|---|
| 30 | 16 | 480 |
480 filas es manejable. Pero imagina hacer un CROSS JOIN entre una tabla de clientes (20.000) y una tabla de productos (50.000): 1.000 millones de filas. Antes de usar CROSS JOIN, siempre calcula mentalmente el tamaño del resultado.
CROSS JOIN accidental
Un error muy común al usar la sintaxis antigua de JOIN (tablas separadas por comas) es olvidar la condición WHERE. Sin ella, obtienes un producto cartesiano no deseado:
-- ERROR común: olvidar la condición WHERE
-- Esto genera 30 × 16 = 480 filas (producto cartesiano completo)
SELECT p.nombre, c.nombre
FROM productos p, categorias c;
-- Lo que realmente querías era:
SELECT p.nombre, c.nombre
FROM productos p, categorias c
WHERE p.categoria_id = c.id;Esta es otra razón por la que la sintaxis moderna con JOIN ... ON es preferible: hace explícita la relación entre tablas y es imposible olvidarla accidentalmente.
Cuándo usar CROSS JOIN
Usa CROSS JOIN cuando necesitas generar explícitamente todas las combinaciones posibles entre dos conjuntos de datos. Los casos más habituales son crear matrices completas para informes (empleado × mes, producto × región), generar calendarios o rangos de fechas combinados con entidades, rellenar datos faltantes en tablas de hechos, y crear datos de prueba con todas las combinaciones posibles.
En todos estos casos, el CROSS JOIN se combina normalmente con un LEFT JOIN posterior para añadir los datos reales donde existan y dejar NULL (o 0) donde no.
Practica con CROSS JOIN
Usa el editor para generar combinaciones con CROSS JOIN:
En el siguiente artículo veremos el Self Join, donde una tabla se une consigo misma para explorar relaciones jerárquicas como la estructura de empleados o las categorías con subcategorías.
Escrito por Eduardo Lázaro
