Как сделать запрос к дополнительному реквизиту договора контрагента в 1С

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

При разработке отчетов и механизмов расчета заработной платы в 1С (например, в конфигурации «Управление нашей фирмой») часто возникает задача отфильтровать данные по дополнительным реквизитам объектов. Например, нам необходимо получить объем продаж по номенклатурной группе «Стекло», но только по тем договорам, за которыми закреплен конкретный менеджер (агент), указанный в дополнительных реквизитах договора контрагента.

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

Постановка задачи и анализ структуры данных

Сформулируем исходные условия. У нас есть регистр накопления Продажи.Обороты. Нам нужно связать его со справочником ДоговорыКонтрагентов, у которого включен механизм дополнительных реквизитов (реализованный на базе Библиотеки Стандартных Подсистем — БСП). Дополнительный реквизит «Агент» (ссылающийся на справочник Сотрудники или ФизическиеЛица) хранится в подчиненной табличной части договора.

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

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

Решение 1. Прямое соединение таблиц (Простой рабочий вариант)

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


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

Проанализируем этот запрос по шагам:

  1. В качестве источника данных мы выбираем табличную часть дополнительных реквизитов договоров: Справочник.ДоговорыКонтрагентов.ДополнительныеРеквизиты.
  2. Мы выполняем ВНУТРЕННЕЕ СОЕДИНЕНИЕ с таблицей Продажи.Обороты. Это автоматически отсекает все продажи по договорам, у которых не заполнен или отсутствует нужный реквизит.
  3. Связь таблиц осуществляется по условию равенства ссылки договора в табличной части и договора из документа-регистратора продаж: ПродажиОбороты.Документ.Договор.
  4. В условие соединения добавлены жесткие фильтры на наименование свойства (Свойство.Наименование = "Агент") и на равенство значения параметру запроса (Значение = &Сотрудник).

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

Анализ проблем производительности и безопасности

Выясним причины, почему первый вариант запроса считается неоптимальным в профессиональной разработке 1С:

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

2. Поиск по строковому представлению свойства
Фильтрация по условию Свойство.Наименование = "Агент" опасна. Во-первых, если пользователь в режиме предприятия переименует этот реквизит (например, исправит опечатку или напишет «Агент (продажи)»), ваш запрос мгновенно сломается. Во-вторых, сравнение текстовых строк в базе данных происходит медленнее, чем сравнение ссылочных уникальных идентификаторов (GUID).

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

Решение 2. Оптимальный и безопасный запрос (Best Practices 1С)

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

Рассмотрим подробнее оптимальный алгоритм работы:

  1. Сначала мы выберем все договоры контрагентов, у которых заполнен реквизит «Агент» нужным нам сотрудником, и поместим этот список во временную таблицу.
  2. Проиндексируем временную таблицу по полю договора для ускорения последующего соединения.
  3. Во втором пакете запроса мы соединим готовую временную таблицу с оборотами продаж. При этом мы обязательно ограничим составной тип поля регистратора с помощью конструкции ВЫРАЗИТЬ или обратимся к измерению напрямую, если оно есть в структуре регистра.

Посмотрим на пример реализации такого запроса:


ВЫБРАТЬ
    ДопРеквизиты.Ссылка КАК Договор
ПОМЕСТИТЬ ВТ_ДоговорыАгента
ИЗ
    Справочник.ДоговорыКонтрагентов.ДополнительныеРеквизиты КАК ДопРеквизиты
ГДЕ
    ДопРеквизиты.Свойство = &Свойство
    И ДопРеквизиты.Значение = &Сотрудник

ИНДЕКСИРОВАТЬ ПО
    Договор
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    СУММА(ПродажиОбороты.КоличествоОборот) КАК КоличествоОборот
ИЗ
    РегистрНакопления.Продажи.Обороты(
        &НачалоПериода,
        &КонецПериода,
        ,
        Номенклатура.КатегорияНоменклатуры.Наименование = "Стекло"
    ) КАК ПродажиОбороты
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДоговорыАгента КАК ВТ_Договоры
        ПО ВТ_Договоры.Договор = ВЫРАЗИТЬ(ПродажиОбороты.Документ КАК Документ.РасходнаяНакладная).Договор

Разберем ключевые улучшения, которые мы внесли в этот запрос:

Альтернативный программный метод с использованием БСП

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

Для быстрого получения значения дополнительного реквизита по конкретному договору можно использовать метод общего модуля:

ЗначениеАгента = УправлениеСвойствами.ЗначениеСвойства(ДоговорСсылка, "Агент");

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

ТаблицаСвойств = УправлениеСвойствами.ЗначенияСвойств(МассивДоговоров, Истина);

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

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

← На главную