Как в SQL связать несколько таблиц последовательно через несколько соединений?

Программист IT и автоматизация бизнеса
← На главную

При разработке запросов к базам данных нередко возникает ситуация, когда необходимо получить реквизит связанной сущности второго уровня (разыменовать поле «через две точки»). Например, из основной таблицы пакетов мы получаем данные ключа, а из таблицы ключей нам требуется извлечь тип записи. Разберем по шагам, как правильно конструировать подобные запросы, нужно ли использовать вложенные конструкции и как обеспечить высокую производительность при обработке миллионов строк.

Базовый синтаксис цепочки соединений

СУБД не ограничивает разработчика связыванием только с ведущей (первой) таблицей в секции FROM. Каждое последующее соединение (LEFT JOIN, INNER JOIN) может ссылаться на поля любой из ранее объявленных в запросе таблиц.

Посмотрим на пример построения последовательного соединения трех таблиц:


SELECT
    CurrentPackages.ID,
    Tab_Key.ID AS KeyID,
    Tab_Type.Name AS RecordTypeName
FROM CurrentPackages
LEFT JOIN KeyValues AS Tab_Key
    ON (CurrentPackages.ID_Key = Tab_Key.ID
        AND CurrentPackages.ID_Source = Tab_Key.ID_Source
        AND CurrentPackages.ID_Destination = Tab_Key.ID_Destination)
LEFT JOIN RecordType AS Tab_Type
    ON (Tab_Key.ID_Type = Tab_Type.ID)

В данном примере таблица RecordType (псевдоним Tab_Type) соединяется непосредственно с таблицей KeyValues (псевдоним Tab_Key) по полю ID_Type. Заворачивать запрос во вложенные подзапросы для этой цели не требуется — оптимизатор СУБД корректно выстроит дерево выполнения запроса.

Особенности разыменования полей в языке запросов 1С

Если мы работаем с платформой 1С:Предприятие, обращение к реквизитам через точку (например, Документ.Контрагент.ОсновнойДоговор.ВалютаВзаиморасчетов) на уровне СУБД автоматически разворачивается в аналогичную цепочку неявных LEFT JOIN. Однако здесь важно учитывать следующее поведение:

  1. Проблема составных типов: Если поле связи имеет составной тип данных, платформа генерирует левые соединения со всеми таблицами типов, входящих в состав, объединяя результат через ветвления CASE. Чтобы избежать деградации производительности, необходимо явно ограничивать тип функцией ВЫРАЗИТЬ(Поле КАК Тип).
  2. Управление соединениями: Явное указание ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ в тексте запроса дает разработчику полный контроль над планом запроса, в отличие от неявных точек в секции ВЫБРАТЬ.

Оптимизация соединений на больших объемах данных

Когда таблицы содержат миллионы записей, прямое соединение может выполняться медленно, если не соблюдены ключевые правила проектирования:

  1. Индексация условий связи: Поля ID, ID_Type, а также поля составных условий (ID_Source, ID_Destination) должны входить в соответствующие индексы. Для составных условий порядок полей в индексе должен соответствовать принципу селективности: наиболее уникальные поля размещаются первыми.
  2. Совпадение типов данных: Типы и разрядность соединяемых колонок должны строго совпадать. Неявное приведение типов (например, сравнение строк разной длины или чисел разной точности) приводит к невозможности использования индексов (нарушение SARGability) и переходу СУБД к полному сканированию таблиц (Table Scan / Index Scan).
  3. Использование временных таблиц вместо вложенных запросов: Если данные требуют предварительной фильтрации или агрегации, на платформе 1С предпочтительно помещать выборку во временную таблицу с помощью конструкции ПОМЕСТИТЬ и выполнять явное индексирование через ИНДЕКСИРОВАТЬ ПО. Вложенные запросы в соединениях скрывают статистику от оптимизатора СУБД, что на миллионных массивах приводит к выбору неоптимального типа соединения (например, ошибочный Nested Loops вместо Hash Match).
  4. Выбор между LEFT JOIN и INNER JOIN: Если бизнес-логика гарантирует, что ссылка не может быть пустой, используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN). Это позволяет СУБД гибко переставлять порядок связывания таблиц для минимизации стоимости плана.
← На главную