SQL-антипаттерны: плохие JOIN’ы и как их избежать

JOIN — один из мощнейших инструментов в SQL. Но с большой силой приходит большая ответственность. Неверное использование JOIN может приводить к дублированию строк, утечке производительности, неявным ошибкам и даже разрушению логики приложения.

В этой статье рассмотрим примеры антипаттернов при использовании JOIN’ов, объясним, почему они плохи, и как их избегать.

JOIN без условия (классический CROSS JOIN по ошибке)

Если вы не указали ON — это не INNER JOIN, а декартово произведение: каждая строка из users будет соединена с каждой строкой из orders.

Почему плохо:

  • Гигантское количество строк на выходе
  • Убийство производительности
  • Часто результат не имеет смысла

Как правильно:


Дублирование данных из-за 1:N без агрегатов или DISTINCT

Запрос:

Если у пользователя несколько заказов — будет несколько строк с одинаковым u.name.

Почему плохо:

  • Дубли могут ввести в заблуждение: кажется, что это ошибка, хотя на самом деле это следствие 1:N связи
  • Лишние строки увеличивают нагрузку на приложение
  • При JOIN’е с большим числом зависимых записей (например, comments, likes) можно получить гигантское кол-во строк

Как правильно:

  • Если нужны только пользователи — можно попробовать DISTINCT, но с осторожностью: он исключит дубли, но может скрыть информацию о количестве связанных записей
  • Если агрегировать — использовать GROUP BY или подзапрос:

LEFT JOIN без необходимости

Запрос:

Если мы хотим только пользователей с заказами — LEFT JOIN избыточен и замедляет запрос.

Почему плохо:

  • LEFT JOIN обрабатывается сложнее, чем INNER JOIN, особенно при большом объёме данных
  • Возвращаются NULL’ы, которые вероятно потом придётся исключать в коде или как условие в WHERE, что может затруднить чтение запроса и ухудшить производительность
  • Запрос может выглядеть корректным, но вести себя не так как задумано: например, возвращать пользователей без заказов, если не фильтровать NULL-значения

Как правильно: Использовать обычный INNER JOIN:


Использование JOIN вместо EXISTS (или IN)

Запрос:

Мы просто хотим выбрать пользователей, у которых есть заказы. Зачем JOIN’ить все строки?

Почему плохо:

  • JOIN приносит лишние данные
  • Возможны дубликаты

Как правильно:

или

IN работает хорошо на небольших подзапросах, особенно если user_id индексирован. Но на больших объёмах EXISTS обычно быстрее, так как останавливается при первом совпадении и лучше оптимизируется планом выполнения.


JOIN по неверному полю или типу

Запрос:

или

В PostgreSQL :: — оператор кастинга (приведения типов).

Например, если user_id — число (integer), а u.id — строка (text), то написав o.user_id::text, мы приводим число к строке для сравнения. Аналог в стандартном SQL: CAST(o.user_id AS VARCHAR).

Почему плохо:

  • Нарушение логики связей между таблицами: поля могут быть случайно схожими по названию, но не несут отношения друг к другу
  • Индексы не используются эффективно при приведении типов (::text и т.п.), что замедляет выполнение запроса
  • Ошибка может не проявляться явно — просто вернётся пустой результат или неполные данные

Как правильно:

  • Проверить семантику полей: действительно ли эти поля логически связаны?
  • Убедиться, что типы совпадают (например, INT и INT, а не INT и VARCHAR)
  • По возможности — не использовать приведение типов в ON-условиях, особенно если важно использовать индексы

Плохие JOIN’ы могут не вызывать ошибок — но они подрывают производительность и создают логические ловушки. Проверяйте:

  • Нужен ли вам JOIN вообще?
  • Правильно ли вы пишете ON?
  • Не дублируются ли строки?
  • Не выгоднее ли использовать EXISTS?

Хорошие JOIN’ы — это не только правильно работающий код, но и читаемый, поддерживаемый, эффективный SQL.

Мало букафф? Читайте есчо !

Нормализация базы данных, разбираемся с нормальными формами на примере

Июль 14, 2025 г.

Нормализация - это поэтапный процесс, происходит по шагам. Каждый шаг — это нормальная форма (НФ), которая обычно требует, чтобы предыдущий этап нормализации был выполнен. Всего их выделяют шесть, но чаще всего достаточно первых четырёх. Я рассмотрю их ...

Читать

НФ не требуют введения суррогатных ключей?

Июль 12, 2025 г.

Сами по себе нормальные формы (НФ) не требуют введения суррогатных (искусственных) ключей, но на практике их часто используют при нормализации, особенно начиная со 2НФ и 3НФ. Давайте разберём подробнее. Что такое суррогатный ключ? Суррогатный ...

Читать

Что такое CROSS JOIN

Июль 4, 2025 г.

Конструкция CROSS JOIN в SQL — это один из видов JOIN, который создаёт декартово произведение двух таблиц: каждая строка из первой таблицы соединяется со всеми строками второй. Если: Таблица A содержит m строк, Таблица B содержит n строк, ...

Читать

Первичный и внешний ключ в SQL : простое объяснение

Июль 7, 2025 г.

Когда вы впервые сталкиваетесь с базами данных, всё кажется немного запутанным: таблицы, строки, какие-то "ключи"... Но если объяснить это на живых примерах — всё становится куда яснее. Давайте разберёмся, что такое первичный ключ, внешний ключ, и ещё ...

Читать
 

Комментарии к «SQL-антипаттерны: плохие JOIN’ы и как их избежать»

Понравилась статья? Есть вопросы? - пишите в комментариях.



Комментарий: