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

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

При разработке отчетов и доработке типовых конфигураций разработчики часто сталкиваются с необходимостью вывести значения дополнительных реквизитов объекта (документа или справочника). Дополнительные реквизиты хранятся в одноименной табличной части объекта ДополнительныеРеквизиты. Если соединить эту табличную часть стандартным левым соединением, но допустить ошибку в условиях фильтрации, строки с незаполненными реквизитами исчезнут из выборки.

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

Выясним причину: почему пропадают документы

Проанализируем типичный ошибочный запрос, с которого начинается разработка подобных отчетов:


ВЫБРАТЬ
    Реализация.Ссылка КАК Накладная,
    Реализация.СуммаДокумента,
    ДопРеквизиты1.Значение КАК Вознаграждение1,
    ДопРеквизиты2.Значение КАК Вознаграждение2
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.ДополнительныеРеквизиты КАК ДопРеквизиты1
        ПО ДопРеквизиты1.Ссылка = Реализация.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.ДополнительныеРеквизиты КАК ДопРеквизиты2
        ПО ДопРеквизиты2.Ссылка = Реализация.Ссылка
ГДЕ
    Реализация.Дата > &Дата
    И ДопРеквизиты1.Свойство.Наименование = "Вознаграждение 1"
    И ДопРеквизиты2.Свойство.Наименование = "Вознаграждение 2"

В этом запросе использовано ЛЕВОЕ СОЕДИНЕНИЕ, однако в секции ГДЕ указано условие на присоединенные таблицы: ДопРеквизиты1.Свойство.Наименование = "Вознаграждение 1".

Когда у документа не заполнен дополнительный реквизит, в результате левого соединения поля таблицы ДопРеквизиты1 заполняются значениями NULL. Затем СУБД применяет условие из секции ГДЕ. Сравнение NULL = "Вознаграждение 1" дает результат ЛОЖЬ. В итоге строка с документом отсекается. Таким образом, любое условие в секции ГДЕ, проверяющее поля правой (присоединяемой) таблицы на конкретное значение, фактически превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

Способ 1. Перенос условий отбора свойств в секцию ПО

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

Посмотрим на исправленный текст запроса:


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

Обратим внимание на две важнейшие доработки по сравнению с исходным кодом:

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

Способ 2. Архитектурная оптимизация: один проход и группировка (Pivot)

Когда требуется извлечь не два, а три, пять или десять дополнительных реквизитов, множественные левые соединения одной и той же физической таблицы резко снижают производительность запроса. Оптимизатор СУБД строит неэффективный план, а объем промежуточных данных растет в геометрической прогрессии.

Рассмотрим более производительный подход: выберем данные табличной части за один проход и развернем реквизиты в строки с помощью агрегатных функций и группировки:


ВЫБРАТЬ
    Реализация.Ссылка КАК Накладная,
    Реализация.СуммаДокумента КАК СуммаДокумента,
    МАКСИМУМ(ВЫБОР
        КОГДА ДопРеквизиты.Свойство = &Свойство1
            ТОГДА ВЫРАЗИТЬ(ДопРеквизиты.Значение КАК Число(15, 2))
        ИНАЧЕ 0
    КОНЕЦ) КАК Вознаграждение1,
    МАКСИМУМ(ВЫБОР
        КОГДА ДопРеквизиты.Свойство = &Свойство2
            ТОГДА ВЫРАЗИТЬ(ДопРеквизиты.Значение КАК Число(15, 2))
        ИНАЧЕ 0
    КОНЕЦ) КАК Вознаграждение2
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.ДополнительныеРеквизиты КАК ДопРеквизиты
        ПО Реализация.Ссылка = ДопРеквизиты.Ссылка
        И ДопРеквизиты.Свойство В (&СписокСвойств)
ГДЕ
    Реализация.Дата > &Дата
СГРУППИРОВАТЬ ПО
    Реализация.Ссылка,
    Реализация.СуммаДокумента

Преимущества такой схемы:

  1. Табличная часть соединяется ровно один раз, независимо от того, сколько свойств нужно извлечь.
  2. В секции ПО накладывается ограничение по массиву свойств: ДопРеквизиты.Свойство В (&СписокСвойств), что исключает попадание в выборку лишних строк.
  3. Исключается риск случайного дублирования строк при пересечении условий соединений.

Способ 3. Получение дополнительных реквизитов через СКД

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

Разберем особенности работы в СКД:

  1. В основном запросе набора данных достаточно выбрать только сам документ и его стандартные реквизиты:
    
    ВЫБРАТЬ
        Реализация.Ссылка КАК Накладная,
        Реализация.СуммаДокумента КАК СуммаДокумента
    ИЗ
        Документ.РеализацияТоваровУслуг КАК Реализация
    ГДЕ
        Реализация.Дата > &Дата
    
  2. Если отчет разработан на базе типовых механизмов БСП (подсистема «Свойства»), подсистема автоматически расширяет поля набора данных СКД. В пользовательских настройках или в схеме компоновки достаточно раскрыть поле Накладная, найти группу Дополнительные реквизиты и выбрать нужные реквизиты («Вознаграждение 1», «Вознаграждение 2»).
  3. При формировании отчета СКД самостоятельно сгенерирует оптимальный SQL-запрос с учетом текущих отборов и выводимых колонок. При этом незаполненные значения автоматически отобразятся пустыми, не исключая строку документа из отчета.
← На главную