Практическая ситуация: запрос обращается к двум таблицам без условия их связывания. Как определяется количество строк результата?
Если запрос выбирает строки из двух источников без условия их связывания, результатом по смыслу является декартово произведение: каждая строка первого источника сочетается с каждой строкой второго. При наличии m строк в первом источнике и n строк во втором результат содержит m × n строк, если последующая фильтрация их не исключает.
Если одна из таблиц пуста, результат также будет пустым. Физически СУБД может выполнить такой запрос не буквальным перебором всех пар, но логический результат определяется именно этим правилом.
В реляционной модели декартово произведение является базовой операцией над отношениями. Оно позволяет построить все возможные пары строк, а соединение обычно рассматривается как произведение с дополнительным условием отбора подходящих пар.
Такой подход отделяет смысл запроса от способа его выполнения. СУБД может использовать индексы, хеширование или другой план, но результат должен соответствовать логике произведения и фильтрации.
Отсутствие условия связывания часто возникает из-за пропущенной части JOIN или неверного имени столбца. Запрос может формально выполниться, но вернуть во много раз больше строк, чем ожидалось.
Это приводит к дублированию данных в отчётах, завышенным агрегатам, высокой нагрузке на память и диск. Особенно опасна ошибка при массовом UPDATE или DELETE: слишком широкий набор строк может быть изменён или удалён.
Сначала определите логические источники строк. Если первый источник содержит 100 строк, а второй — 20, отсутствие ограничения между ними даёт до 2000 пар до применения других фильтров.
Условие связывания превращает произведение в более узкое соединение. Например, при сопоставлении клиента с его заказами нужно явно указать связь по ключам:
Важно различать отсутствие условия соединения и условие, которое всегда истинно: оба случая дают декартово произведение. Фильтр в WHERE может позднее убрать часть пар, но не меняет того, что логически сначала формируются возможные комбинации источников, а затем применяется фильтрация.
Количество строк после соединения нельзя всегда вычислить простым умножением: это зависит от совпадений в условии. При этом декартово произведение без фильтра имеет предсказуемый размер m × n.
В отчёте требовалось показать клиентов и их заказы. Из-за пропущенного условия соединения таблица из 50 000 клиентов сочеталась с таблицей из 2 000 000 заказов, поэтому запрос логически формировал до 100 миллиардов пар и завершался ошибкой по времени выполнения.
Рассматривались два варианта. Можно было добавить фильтр по идентификаторам в WHERE, но явное JOIN ... ON лучше показывает назначение связи и снижает риск случайного превращения внешнего соединения во внутреннее при дальнейших изменениях.
Выбрали явное соединение по внешнему ключу и отдельно проверили план выполнения и контрольное количество строк. После исправления результат содержал только пары «клиент — его заказ», а агрегаты перестали завышаться.
1. Всегда ли СУБД физически создаёт все пары перед фильтрацией?
Нет. Декартово произведение описывает логическую семантику, а не обязательный физический алгоритм. Оптимизатор может применить предикаты раньше, выбрать индексное соединение или другой эквивалентный план. Однако итоговый результат должен быть таким, будто логические операции выполнены в установленном порядке.
2. Что произойдёт, если один источник содержит одну строку?
Декартово произведение с одним источником из одной строки сохраняет количество строк второго источника: 1 × n = n. Значения этой единственной строки будут повторяться в каждой результирующей паре. Если единственный источник пуст, результат пуст независимо от размера второго источника.
3. Почему случайное декартово произведение особенно опасно для агрегатов?
Потому что одна строка может участвовать во множестве пар, и агрегат будет считать пары, а не исходные сущности. Например, сумма заказа после ошибочного соединения с несколькими строками другой таблицы может быть повторена несколько раз. Исправление требует не только добавить условие соединения, но и проверить кардинальность связи и смысл агрегирования.