В чем разница между INNER JOIN и CROSS JOIN в PostgreSQL и когда кросс-джоин может случайно «положить» базу?
INNER JOIN и CROSS JOIN — два принципиально разных типа соединения таблиц в PostgreSQL, хотя на первый взгляд они могут казаться похожими.
**INNER JOIN** возвращает только те строки, для которых выполняется условие соединения (ON или USING). То есть из двух таблиц берутся только совпадающие пары строк. Например, если таблица orders содержит 1000 строк, а таблица customers — 500, то INNER JOIN вернёт ровно столько строк, сколько есть совпадений по ключу. Это управляемый, предсказуемый результат.
**CROSS JOIN** (декартово произведение) возвращает все возможные комбинации строк из двух таблиц без какого-либо условия фильтрации. Если таблица A содержит N строк, а таблица B — M строк, результат будет содержать N × M строк. Условие ON для CROSS JOIN не предусмотрено — это принципиальное архитектурное отличие.
**Синтаксис:**
sql
— INNER JOIN
SELECT * FROM orders o
INNER JOIN customers c ON o.customer_id = c.id;
— CROSS JOIN
SELECT * FROM colors
CROSS JOIN sizes;
**Когда CROSS JOIN уместен:**
— Генерация всех комбинаций (например, цвета × размеры для каталога товаров).
— Создание тестовых наборов данных.
— Построение матриц расписаний или сеток дат.
**Когда CROSS JOIN «кладёт» базу:**
Проблема возникает, когда разработчик случайно создаёт декартово произведение. Это может произойти в нескольких сценариях:
1. **Забытое условие WHERE или ON.** Если написать `SELECT * FROM table_a, table_b` без условия соединения — это неявный CROSS JOIN. При таблицах по 100 000 строк результат составит 10 миллиардов строк.
2. **Ошибка в условии JOIN.** Если условие ON всегда истинно (например, `ON 1=1`), INNER JOIN превращается в CROSS JOIN.
3. **Соединение с подзапросом без корреляции.** Некоррелированный подзапрос в FROM без явного условия соединения даёт декартово произведение.
4. **Большие таблицы.** Таблица на 1 млн строк × таблица на 1 млн строк = 10^12 строк. PostgreSQL начнёт выгружать данные на диск, исчерпает оперативную память, заблокирует другие запросы и фактически сделает базу недоступной.
**Как защититься:**
— Всегда явно указывайте условия JOIN.
— Используйте `EXPLAIN ANALYZE` перед выполнением тяжёлых запросов — в плане будет видно `Nested Loop` с огромным estimated rows.
— Настройте `statement_timeout` и `work_mem` в PostgreSQL.
— Включайте `log_min_duration_statement` для мониторинга долгих запросов.
— В продакшене используйте `pg_stat_activity` для отслеживания и отмены зависших запросов через `pg_cancel_backend()`.
Таким образом, INNER JOIN — это инструмент для фильтрованного соединения по ключу, а CROSS JOIN — специализированный инструмент для генерации комбинаций, который при неосторожном применении способен мгновенно перегрузить даже мощный сервер баз данных.
