← Назад к вопросам

Сколько записей возвращает CROSS JOIN?

1.0 Junior🔥 141 комментариев
#SQL и базы данных

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сколько записей возвращает CROSS JOIN?

Короткий ответ

CROSS JOIN возвращает M × N записей, где:

  • M = количество строк в левой таблице
  • N = количество строк в правой таблице

Это декартов (картезианский) произведение — каждая строка левой таблицы соединяется с каждой строкой правой таблицы.

Пример

-- Таблица 1: products (3 строки)
SELECT * FROM products;
-- product_id | name
-- 1           | Laptop
-- 2           | Mouse
-- 3           | Keyboard

-- Таблица 2: colors (2 строки)
SELECT * FROM colors;
-- color_id | color_name
-- 1         | Black
-- 2         | Silver

-- CROSS JOIN: 3 × 2 = 6 строк
SELECT p.product_id, p.name, c.color_id, c.color_name
FROM products p
CROSS JOIN colors c;

-- Результат:
-- product_id | name     | color_id | color_name
-- 1           | Laptop   | 1        | Black
-- 1           | Laptop   | 2        | Silver
-- 2           | Mouse    | 1        | Black
-- 2           | Mouse    | 2        | Silver
-- 3           | Keyboard | 1        | Black
-- 3           | Keyboard | 2        | Silver

Синтаксис CROSS JOIN

Существует несколько способов написать CROSS JOIN:

-- Способ 1: Явный CROSS JOIN (рекомендуется)
SELECT *
FROM products p
CROSS JOIN colors c;

-- Способ 2: Неявный (через запятую в FROM)
-- ⚠️ Опасный способ, потому что не видно, что это CROSS JOIN
SELECT *
FROM products p, colors c;

-- Способ 3: ON 1=1 (старый стиль, избегай)
SELECT *
FROM products p
INNER JOIN colors c ON 1=1;

Когда использовать CROSS JOIN

1. Генерация всех комбинаций

-- Задача: сделать полный каталог (все продукты во всех цветах)
SELECT 
  p.product_id,
  p.name,
  c.color_name,
  CONCAT(p.name, ' - ', c.color_name) as product_variant
FROM products p
CROSS JOIN colors c
ORDER BY p.product_id, c.color_id;

-- Полезно для e-commerce: из 100 продуктов × 5 цветов = 500 вариантов SKU

2. Генерация дат

-- Задача: создать календарь (все комбинации дат × справочников)
WITH dates AS (
  SELECT CURRENT_DATE - INTERVAL '90 days' + INTERVAL '1 day' * day as date
  FROM GENERATE_SERIES(0, 90) as day_series(day)
),
stores AS (
  SELECT store_id FROM stores WHERE status = 'active'
)
SELECT d.date, s.store_id
FROM dates d
CROSS JOIN stores s;

-- Результат: для каждого дня × каждый магазин = 90 × 50 = 4500 строк
-- Это основа для fill down логики (заполнение нулей в продажах)

3. Матрица повторений

-- Задача: у тебя есть пользователи и месяцы
-- Нужна матрица: каждый пользователь × каждый месяц

WITH users AS (
  SELECT DISTINCT user_id FROM orders
),
months AS (
  SELECT DISTINCT DATE_TRUNC('month', order_date) as month FROM orders
)
SELECT u.user_id, m.month
FROM users u
CROSS JOIN months m
LEFT JOIN orders o ON u.user_id = o.user_id AND DATE_TRUNC('month', o.order_date) = m.month
ORDER BY u.user_id, m.month;

-- Результат: even if user didn't order in month X, все равно показываем row
-- Это основа для вычисления retention metrics

Осторожно: CROSS JOIN может быть опасен!

Проблема 1: Экспоненциальный рост

Левая таблица: 1,000 строк
Правая таблица: 1,000 строк
CROSS JOIN: 1,000,000 строк ✓ OK

Левая таблица: 10,000 строк
Правая таблица: 100,000 строк
CROSS JOIN: 1,000,000,000 строк ⚠️ Может упасть!

Левая таблица: 1,000,000 строк
Правая таблица: 1,000 строк
CROSS JOIN: 1,000,000,000 строк 🔴 Почти наверняка упадёт!

Решение: Проверяй количество строк перед CROSS JOIN

-- ДО выполнения большого CROSS JOIN проверь размеры
SELECT 
  (SELECT COUNT(*) FROM table1) as table1_count,
  (SELECT COUNT(*) FROM table2) as table2_count,
  (SELECT COUNT(*) FROM table1) * (SELECT COUNT(*) FROM table2) as result_count;

-- Если result_count > 100 млн — пересмотри логику

Проблема 2: Случайный CROSS JOIN (скрытый баг)

-- Плохо: забыл WHERE clause или JOIN условие
SELECT o.order_id, p.price
FROM orders o
INNER JOIN prices p ON 1=1;  -- 🔴 Это CROSS JOIN!
-- Результат: 1M заказов × 1000 цен = 1B строк

-- Хорошо: явное условие
SELECT o.order_id, p.price
FROM orders o
INNER JOIN prices p ON o.product_id = p.product_id
  AND o.order_date BETWEEN p.valid_from AND p.valid_to;

Проблема 3: CROSS JOIN в WHERE

-- Очень плохо
SELECT *
FROM orders o
WHERE o.order_id IN (
  SELECT p1.product_id
  FROM products p1
  CROSS JOIN products p2  -- Зачем?
);

-- Это создаст 1000 × 1000 = 1M комбинаций в subquery!

Практический пример: Retention Cohort Analysis

Это real-world сценарий, где CROSS JOIN незаменим:

-- Задача: посчитать retention по когортам (когда пользователь первый раз активен)
WITH cohorts AS (
  -- Когорта: первый месяц активности пользователя
  SELECT 
    user_id,
    DATE_TRUNC('month', MIN(order_date)) as cohort_month
  FROM orders
  GROUP BY user_id
),
activity_matrix AS (
  -- Матрица: каждый user × каждый месяц
  -- Потому что нужно показать даже месяцы без активности
  SELECT DISTINCT 
    c.user_id,
    DATE_TRUNC('month', o.order_date) as activity_month
  FROM cohorts c
  CROSS JOIN orders o  -- ← Вот он, CROSS JOIN!
),
retention AS (
  SELECT 
    c.cohort_month,
    EXTRACT(MONTH FROM a.activity_month - c.cohort_month) as month_offset,
    COUNT(DISTINCT c.user_id) as retained_users
  FROM cohorts c
  LEFT JOIN activity_matrix a ON c.user_id = a.user_id
  GROUP BY c.cohort_month, month_offset
  ORDER BY c.cohort_month, month_offset
)
SELECT * FROM retention;

Производительность

-- CROSS JOIN медленнее чем обычный JOIN
-- Потому что:
-- 1. Нет фильтрации по условию (ON clause)
-- 2. Все комбинации должны быть вычислены
-- 3. Результат большой

-- Бенчмарк (100k левая × 1k правая):
-- INNER JOIN с условием: 200ms
-- CROSS JOIN: 5-10s

-- Оптимизация:
-- 1. Предфильтруй таблицы перед CROSS JOIN
SELECT *
FROM (
  SELECT * FROM products WHERE category = 'Electronics'  -- 100 строк
) p
CROSS JOIN (
  SELECT * FROM colors WHERE is_active = true  -- 5 строк
) c;
-- Вместо: 1000 × 100 = 100k
-- Теперь: 100 × 5 = 500

-- 2. Используй LIMIT если не нужны все комбинации
SELECT TOP 1000 * FROM products p CROSS JOIN colors c;

Ошибки при CROSS JOIN

Ошибка 1: Дублирование из-за забытого JOIN условия

-- Плохо
SELECT o.order_id, od.product_id
FROM orders o
CROSS JOIN order_details od;  -- Каждый заказ × ВСЕ order details!

-- Хорошо
SELECT o.order_id, od.product_id
FROM orders o
INNER JOIN order_details od ON o.order_id = od.order_id;

Ошибка 2: Неправильный расчёт в итоговых метриках

-- Плохо: если использовать CROSS JOIN без DISTINCT
SELECT COUNT(*) as total_revenue
FROM orders o
CROSS JOIN order_details od
WHERE o.amount > 100;
-- Каждый заказ > 100 будет посчитан МНОГО раз!

-- Хорошо: используй GROUP BY
SELECT SUM(o.amount) as total_revenue
FROM orders o
INNER JOIN order_details od ON o.order_id = od.order_id
GROUP BY o.order_id;

Правило: Когда использовать CROSS JOIN

✅ Использовать CROSS JOIN когда:
  - Явно нужны ВСЕ комбинации
  - Обе таблицы маленькие (< 10k строк)
  - Пишешь матрицу для дальнейших расчётов
  - Генерируешь справочники (календари, комбинации)

❌ Избегать CROSS JOIN когда:
  - Забыл WHERE или ON условие (ошибка!)
  - Одна таблица большая (> 100k)
  - Нужен обычный JOIN (с условием)
  - Не уверен в результирующем размере

Вывод

CROSS JOIN возвращает M × N записей — декартово произведение. Это мощный инструмент для:

  • Генерации комбинаций
  • Создания матриц для анализа
  • Cohort analysis и retention расчётов

Но нужна осторожность: легко создать 1 млрд строк по ошибке!