Как оптимизировать запрос к регистру партий с получением количества из документа поступления в УТ 10.3?

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

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

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

Анализ типичных ошибок и причин замедления запроса

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


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

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

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

Шаг 1. Оптимизация виртуальной таблицы регистра

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

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


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

Шаг 2. Использование временных таблиц и приведение типов

Чтобы избежать блокировок и неоптимальных планов выполнения СУБД, разделим сбор данных на этапы с использованием временных таблиц. Также применим функцию ВЫРАЗИТЬ для поля составного типа и сгруппируем табличную часть документа поступления, чтобы исключить дубли строк.

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


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

СГРУППИРОВАТЬ ПО
    ВЫРАЗИТЬ(Партии.ДокументОприходования КАК ДокументСсылка.ПоступлениеТоваровУслуг),
    Партии.Номенклатура

ИНДЕКСИРОВАТЬ ПО
    ДокументПоступления,
    Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ПартииТоваров.ДокументПоступления КАК ДокументОприходования,
    ВТ_ПартииТоваров.КоличествоПриход КАК КоличествоПриход,
    ВТ_ПартииТоваров.СтоимостьПриход КАК СтоимостьПриход,
    ЕСТЬNULL(ПоступлениеТовары.КоличествоВПоступлении, 0) КАК КоличествоВПоступлении
ИЗ
    ВТ_ПартииТоваров КАК ВТ_ПартииТоваров
        ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
            ТЧ.Ссылка КАК Ссылка,
            ТЧ.Номенклатура КАК Номенклатура,
            СУММА(ТЧ.Количество * ТЧ.Коэффициент) КАК КоличествоВПоступлении
        ИЗ
            Документ.ПоступлениеТоваровУслуг.Товары КАК ТЧ
        ГДЕ
            ТЧ.Ссылка В
                (ВЫБРАТЬ
                    ВТ.ДокументПоступления
                ИЗ
                    ВТ_ПартииТоваров КАК ВТ)
            И ТЧ.Номенклатура = &Номенклатура
        СГРУППИРОВАТЬ ПО
            ТЧ.Ссылка,
            ТЧ.Номенклатура) КАК ПоступлениеТовары
        ПО ВТ_ПартииТоваров.ДокументПоступления = ПоступлениеТовары.Ссылка
            И ВТ_ПартииТоваров.Номенклатура = ПоступлениеТовары.Номенклатура

Особенности учета единиц измерения и коэффициентов в УТ 10.3

Рассмотрим важный нюанс прикладного решения «Управление торговлей 10.3». В табличной части Товары документов количество хранится в тех единицах измерения, которые выбрал пользователь (реквизит ЕдиницаИзмерения), а реальный пересчет в базовые единицы хранения остатков осуществляется через реквизит Коэффициент.

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

СУММА(ТЧ.Количество * ТЧ.Коэффициент) КАК КоличествоВПоступлении

Рекомендации для работы в форме подбора

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

  1. Откажитесь от лишних соединений «на лету»: Если пользователю требуется лишь выбрать партию, отображайте данные остатков и партий быстрым запросом к регистру. Данные самого документа поступления можно подтягивать только в момент непосредственного переноса позиции в итоговый документ.
  2. Используйте пакетную выборку: Если данные по первичному документу всё же необходимы на форме сразу, получайте их порционно для всего видимого списка номенклатуры, передавая массив ссылок в параметр &Номенклатура, а не выполняя запрос в цикле по каждой позиции.
← На главную