Как правильно объединить данные двух регистров в запросе 1С и почему нельзя использовать вложенные запросы

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

В практике разработки на платформе 1С:Предприятие часто возникает задача объединения данных из нескольких источников. Типичный сценарий: у нас есть основной регистр (например, со списком подразделений) и вспомогательный (с финансовыми показателями). Задача состоит в том, чтобы получить полный список подразделений и дополнить его суммами там, где они есть. Рассмотрим, как решить эту задачу эффективно и в соответствии со стандартами разработки.

Разбор логики соединений: почему именно ЛЕВОЕ СОЕДИНЕНИЕ?

Для начала проанализируем ситуацию. У нас есть первая таблица (10 записей) и вторая таблица (5 записей). Чтобы на выходе получить именно 10 строк, нам необходимо использовать ЛЕВОЕ СОЕДИНЕНИЕ. В этом случае система берет все записи из «левой» (первой) таблицы и ищет соответствия в «правой» (второй) таблице по заданному условию связи.

Разберем по шагам, как это работает:

  1. Система обращается к первой таблице и выбирает все записи.
  2. Для каждой строки из первой таблицы производится поиск во второй таблице по полю связи (например, Наименование).
  3. Если соответствие найдено, данные из второй таблицы подставляются в строку.
  4. Если соответствие не найдено, поля второй таблицы заполняются значением NULL.

Важно помнить: если вы перепутаете таблицы местами или используете ВНУТРЕННЕЕ СОЕДИНЕНИЕ, вы получите только те 5 записей, которые есть в обоих регистрах. Именно поэтому выбор правильного типа связи критичен для полноты данных.

Проблема вложенных запросов и современные стандарты

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

  1. Оптимизация СУБД: Современным системам управления базами данных (MS SQL, PostgreSQL) крайне сложно строить эффективный план выполнения для запросов с вложенными подзапросами. Это часто приводит к «сканированию» всей таблицы вместо использования индексов.
  2. Отсутствие индексации: Результат вложенного запроса невозможно проиндексировать. Если во вложенном запросе выбирается 100 000 строк, то при соединении с основной таблицей производительность упадет в геометрической прогрессии.
  3. Читаемость кода: Сопровождать запрос с вложенностью в 3-4 уровня практически невозможно.

Выясним, как сделать правильно. Вместо вложенных запросов стандарты 1С предписывают использовать Временные Таблицы.

Реализация через временные таблицы и индексацию

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

Проанализируем пример кода:


// Этап 1: Выбираем данные первого регистра во временную таблицу
ВЫБРАТЬ
    РегистрПодразделения.Наименование КАК Наименование,
    РегистрПодразделения.Подразделение КАК Подразделение
ПОМЕСТИТЬ ВТ_ПервыйРегистр
ИЗ
    РегистрСведений.РегистрПодразделения КАК РегистрПодразделения
;

////////////////////////////////////////////////////////////////////////////////
// Этап 2: Выбираем данные второго регистра и ИНДЕКСИРУЕМ поле связи
ВЫБРАТЬ
    РегистрСуммы.Наименование КАК Наименование,
    РегистрСуммы.Контрагент КАК Контрагент,
    РегистрСуммы.Сумма КАК Сумма
ПОМЕСТИТЬ ВТ_ВторойРегистр
ИЗ
    РегистрСведений.РегистрСуммы КАК РегистрСуммы
ИНДЕКСИРОВАТЬ ПО
    Наименование
;

////////////////////////////////////////////////////////////////////////////////
// Этап 3: Итоговое левое соединение
ВЫБРАТЬ
    ВТ_ПервыйРегистр.Подразделение,
    ВТ_ПервыйРегистр.Наименование,
    ЕСТЬNULL(ВТ_ВторойРегистр.Контрагент, ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)) КАК Контрагент,
    ЕСТЬNULL(ВТ_ВторойРегистр.Сумма, 0) КАК Сумма
ИЗ
    ВТ_ПервыйРегистр КАК ВТ_ПервыйРегистр
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВторойРегистр КАК ВТ_ВторойРегистр
        ПО ВТ_ПервыйРегистр.Наименование = ВТ_ВторойРегистр.Наименование

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

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

Посмотрим, как этого избежать с помощью функции ЕСТЬNULL. Проанализируем два случая:

  1. Числовые показатели: Используйте ЕСТЬNULL(Поле, 0). Это позволит корректно суммировать данные в итогах отчета.
  2. Ссылочные типы: Для полей типа СправочникСсылка лучше использовать ЕСТЬNULL(Поле, ЗНАЧЕНИЕ(Справочник.ИмяСправочника.ПустаяСсылка)). Это гарантирует, что в результате всегда будет объект (хоть и пустой), а не отсутствие значения.

Риски при использовании поля «Наименование» для связи

В обсуждаемом примере связь идет по полю Наименование. Рассмотрим, почему в реальных задачах этого следует избегать:

Во-первых, строковые сравнения в SQL работают значительно медленнее, чем сравнения по ссылкам (GUID). Даже если поле проиндексировано, производительность будет ниже.

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

Рекомендация: Всегда старайтесь связывать таблицы по уникальным идентификаторам (ссылкам) или кодам, имеющим строгий формат.

Итоговые выводы

Для решения задачи по «дополнению» данных мы проанализировали следующие принципы:

Соблюдение этих простых правил не только решит текущую техническую проблему, но и сделает ваш код соответствующим профессиональным стандартам разработки 1С.

← На главную