Как связать два справочника через общий реквизит в третьем справочнике в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке сложных отчетов или обработок в системе 1С:Предприятие 8 мы часто сталкиваемся с ситуацией, когда данные распределены по разным объектам метаданных. Классический пример: у нас есть два справочника, которые напрямую не связаны друг с другом, но оба они имеют ссылки на некий третий справочник. Наша задача — объединить их в одном запросе, чтобы получить сводную информацию. В этой статье мы подробно разберем, как организовать такую связь с помощью Конструктора запросов и программного кода, а также обсудим вопросы производительности.

Анализ структуры связи через посредника

Представим ситуацию: у нас есть Справочник1 и Справочник2. В обоих справочниках есть реквизит (например, ОбъектСвязи), который ссылается на Справочник3. Нам нужно получить данные из первого и второго справочников в одной строке результата запроса, сопоставив их по этому общему полю. По сути, Справочник3 выступает здесь в роли «мостика» или связующего звена.

Рассмотрим по шагам, как это реализовать наиболее эффективно.

Решение 1: Использование Конструктора запросов

Для большинства разработчиков Конструктор запросов является основным инструментом. Чтобы настроить связь через «посредника», выполним следующие действия:

  1. Откроем Конструктор запросов и перейдем на закладку Таблицы и поля.
  2. Добавим в список выбранных таблиц все три справочника: Справочник1, Справочник2 и Справочник3.
  3. Перейдем на закладку Связи. Здесь крайне важно соблюдать последовательность, чтобы система правильно построила дерево соединений.
  4. Сначала свяжем Справочник1 со Справочником3. Укажем условие связи: Справочник1.ОбъектСвязи = Справочник3.Ссылка.
  5. Затем добавим вторую связь: Справочник2 свяжем с тем же Справочником3. Условие будет аналогичным: Справочник2.ОбъектСвязи = Справочник3.Ссылка.
  6. Тип соединения обычно выбирается Левое соединение, чтобы не потерять данные из основной таблицы, если в зависимых таблицах не будет найдено соответствий.

Важный момент: если мы используем левое соединение, то «ведущей» таблицей должна выступать та, из которой нам гарантированно нужны все записи.

Решение 2: Написание запроса вручную (SQL-подобный синтаксис)

Иногда быстрее и нагляднее написать текст запроса самостоятельно. Разберем пример кода, который реализует последовательное соединение трех таблиц:


ВЫБРАТЬ
    Спр1.Ссылка КАК Ссылка1,
    Спр2.Ссылка КАК Ссылка2,
    Спр3.Ссылка КАК ОбщийОбъект
ИЗ
    Справочник.Справочник1 КАК Спр1
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник3 КАК Спр3
        ПО Спр1.СвязующийРеквизит = Спр3.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник2 КАК Спр2
        ПО Спр2.СвязующийРеквизит = Спр3.Ссылка

Проанализируем этот код: мы сначала берем данные из первого справочника, «приклеиваем» к нему запись из третьего по равенству ссылок, а затем к полученному результату присоединяем данные из второго справочника. Таким образом, в одной строке мы получаем информацию из всех трех источников.

Проблема разыменования полей (обращение через точку)

Существует и альтернативный способ получения данных — так называемое «разыменование» или обращение через точку: Справочник1.СвязующийРеквизит.Наименование. Рассмотрим, почему это важно.

С точки зрения простоты написания, обращение через точку кажется более удобным. Однако на уровне СУБД каждое такое обращение превращается в неявное LEFT JOIN. Если в одном запросе вы обращаетесь через точку к десяти разным полям, количество соединений в итоговом SQL-запросе может стать избыточным.

Рекомендация: в профессиональной разработке принято использовать явные соединения на закладке Связи. Это позволяет точно контролировать, какое соединение используется (внутреннее или левое), и оптимизирует план выполнения запроса в SQL Server или PostgreSQL.

Оптимизация производительности: Временные таблицы

Если справочники содержат сотни тысяч записей, прямая связь через три таблицы может работать медленно. Выясним причину: СУБД бывает сложно построить оптимальный путь обхода данных при множественных соединениях. В таких случаях мы используем временные таблицы.

Разберем алгоритм оптимизации:

  1. Выбираем во временную таблицу (например, ВТ_СвязующееЗвено) данные из Справочника3, предварительно отфильтровав их, если это возможно.
  2. Обязательно индексируем временную таблицу по полю, по которому будет идти связь.
  3. В основном запросе соединяем наши справочники уже с проиндексированной временной таблицей.

Пример использования индексации во временной таблице:


ВЫБРАТЬ
    Спр3.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Объекты
ИЗ
    Справочник.Справочник3 КАК Спр3
ИНДЕКСИРОВАТЬ ПО
    Ссылка
;
ВЫБРАТЬ
    Спр1.Ссылка,
    Спр2.Ссылка
ИЗ
    Справочник.Справочник1 КАК Спр1
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Объекты КАК ВТ
        ПО Спр1.СвязующийРеквизит = ВТ.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник2 КАК Спр2
        ПО Спр2.СвязующийРеквизит = ВТ.Ссылка

Обработка пустых значений (NULL)

При использовании левого соединения мы неизбежно столкнемся с тем, что для некоторых записей не будет найдено соответствие в другой таблице. В этом случае система вернет значение NULL.

Чтобы результат запроса был корректным и не вызывал ошибок в дальнейшем (например, при расчетах), мы рекомендуем использовать функцию ЕСТЬNULL. Посмотрим на пример:

ЕСТЬNULL(Справочник2.Количество, 0) КАК Количество

Эта функция проверяет: если в поле пришел NULL, она заменит его на указанное значение (в данном случае на 0 или на пустую ссылку). Это критически важно для корректного отображения данных в отчетах СКД.

Работа с составными типами данных

Ситуация усложняется, если реквизит СвязующийРеквизит имеет составной тип (ссылается на несколько разных справочников). В этом случае обычное соединение будет крайне неэффективным, так как СУБД придется проверять все возможные таблицы, входящие в состав типа.

Проанализируем решение: в таких случаях необходимо использовать конструкцию ВЫРАЗИТЬ(Поле КАК Тип). Это «подсказывает» системе, к какой конкретной таблице нужно обращаться, и значительно ускоряет выполнение запроса.

Пример: ПО ВЫРАЗИТЬ(Спр1.Объект КАК Справочник.Справочник3) = Спр3.Ссылка.

Заключение

Организация связи между справочниками через «посредника» — это стандартная задача, которая требует аккуратности при настройке соединений. Мы рассмотрели, как реализовать это через Конструктор и код, а также узнали, что использование временных таблиц, индексации и явного приведения типов позволяет избежать проблем с производительностью даже на очень больших базах данных.

← На главную