Как упростить запрос и улучшить его читаемость при выборе данных партнера головного контрагента

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

Анализ исходной проблемы

Разберем ситуацию, когда в исходном запросе используется многократное повторение конструкции ВЫБОР КОГДА ... ТОГДА для каждого извлекаемого поля (Ссылка, Код, Наименование). Проанализируем типичный пример «нечитаемого» кода:


ВЫБОР 
    КОГДА Продажи.АналитикаУчетаПоПартнерам.Контрагент.ГоловнойКонтрагент ЕСТЬ NULL 
    ТОГДА Продажи.АналитикаУчетаПоПартнерам.Контрагент.Партнер.Ссылка 
    ИНАЧЕ Продажи.АналитикаУчетаПоПартнерам.Контрагент.ГоловнойКонтрагент.Партнер.Ссылка 
КОНЕЦ КАК Партнер, 
ВЫБОР 
    КОГДА Продажи.АналитикаУчетаПоПартнерам.Контрагент.ГоловнойКонтрагент ЕСТЬ NULL 
    ТОГДА Продажи.АналитикаУчетаПоПартнерам.Контрагент.Партнер.Код 
    ИНАЧЕ Продажи.АналитикаУчетаПоПартнерам.Контрагент.ГоловнойКонтрагент.Партнер.Код 
КОНЕЦ КАК ПартнерКод

Такой подход делает запрос избыточным, трудночитаемым и, что немаловажно, тяжелым для СУБД из-за многократного «разыменования» полей через точку. Выясним причину, почему это плохо: каждое обращение через точку в запросе 1С на уровне SQL превращается в LEFT JOIN. В примере выше для каждого поля система будет заново пытаться соединиться с таблицами контрагентов и партнеров.

Решение №1: Использование функции ЕСТЬNULL и разыменование ссылки

Посмотрим на более компактный вариант написания того же условия. Вместо длинных конструкций ВЫБОР можно использовать функцию ЕСТЬNULL. Это сократит объем кода, хотя и не избавит полностью от проблем с производительностью при большом количестве полей.


(ЕСТЬNULL(
    Продажи.АналитикаУчетаПоПартнерам.Контрагент.ГоловнойКонтрагент.Партнер, 
    Продажи.АналитикаУчетаПоПартнерам.Контрагент.Партнер
)) КАК Партнер, 

(ЕСТЬNULL(
    Продажи.АналитикаУчетаПоПартнерам.Контрагент.ГоловнойКонтрагент.Партнер, 
    Продажи.АналитикаУчетаПоПартнерам.Контрагент.Партнер
)).Код КАК ПартнерКод

Важный нюанс: такая конструкция будет работать корректно только в том случае, если ГоловнойКонтрагент действительно равен NULL (например, при левом соединении). Если же реквизит в базе просто не заполнен, там может храниться Значение(Справочник.Контрагенты.ПустаяСсылка). В этом случае ЕСТЬNULL не сработает, так как пустая ссылка — это не NULL.

Решение №2: Использование временных таблиц (Лучшая практика)

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

Рассмотрим этот алгоритм по шагам:

  1. Создаем временную таблицу ВТ_Партнеры, где для каждого контрагента определяем одну целевую ссылку на партнера.
  2. В основном запросе соединяем таблицу данных с этой временной таблицей.
  3. Выбираем все необходимые реквизиты (Код, Наименование) уже напрямую из справочника партнеров.

Посмотрим на пример кода:


// Шаг 1: Подготовка связей во временной таблице
ВЫБРАТЬ
    Контрагенты.Ссылка КАК Контрагент,
    ЕСТЬNULL(Контрагенты.ГоловнойКонтрагент.Партнер, Контрагенты.Партнер) КАК ЦелевойПартнер
ПОМЕСТИТЬ ВТ_СвязиПартнеров
ИЗ
    Справочник.Контрагенты КАК Контрагенты;

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

Этот метод значительно повышает читаемость и позволяет избежать дублирования логики ВЫБОР для каждого отдельного поля. Кроме того, это оптимизирует нагрузку на сервер базы данных.

Решение №3: Подход через левое соединение (JOIN)

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

Разберем ситуацию на примере:


ВЫБРАТЬ
    ЕСТЬNULL(ПартнерыГоловные.Ссылка, ПартнерыТекущие.Ссылка) КАК Партнер,
    ЕСТЬNULL(ПартнерыГоловные.Код, ПартнерыТекущие.Код) КАК ПартнерКод
ИЗ
    РегистрНакопления.Продажи КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты 
        ПО Продажи.АналитикаУчетаПоПартнерам.Контрагент = Контрагенты.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры КАК ПартнерыТекущие 
        ПО Контрагенты.Партнер = ПартнерыТекущие.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры КАК ПартнерыГоловные 
        ПО Контрагенты.ГоловнойКонтрагент.Партнер = ПартнерыГоловные.Ссылка

Этот способ позволяет избавиться от многострочных ВЫБОР, заменяя их на простые ЕСТЬNULL. Однако стоит помнить, что большое количество соединений также может влиять на скорость выполнения.

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

При работе с крупными базами данных (например, 1С:ERP или УТ 11) важно учитывать следующие моменты:

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

Шаблонизация текста запроса: стоит ли?

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


ТекстЗапроса = "ВЫБРАТЬ " + ПолучитьКусокЗапроса("Ссылка") + " КАК Партнер";

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

Использование в Системе компоновки данных (СКД)

Если вы разрабатываете отчет на СКД, задачу можно упростить еще сильнее. Вы можете вычислить только ссылку на нужного партнера в запросе или через «Вычисляемые поля», а извлечение Кода и Наименования доверить самой системе компоновки. Для этого достаточно в настройках отчета просто выбрать поля НужныйПартнер.Код и НужныйПартнер.Наименование. СКД автоматически и максимально оптимально добавит необходимые соединения в итоговый SQL-запрос.

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

← На главную