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

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

Одной из наиболее распространенных проблем производительности в интерфейсе обычных форм 1С является медленная работа справочника Номенклатура при попытке вывести в него остатки товаров в реальном времени. Основная причина кроется в механике работы события ПриПолученииДанных. Это событие вызывается каждый раз, когда пользователь прокручивает список, открывает форму или меняет размер окна. Если внутри этого события выполняется «тяжелый» запрос к базе данных, интерфейс начинает заметно «тормозить».

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

Почему стандартный подход вызывает тормоза?

Проанализируем типичный код, который часто встречается в конфигурациях. Проблема заключается в том, что запрос выполняется для каждой «пачки» строк, которые отрисовываются на экране. Если в коде присутствуют методы НайтиПоКоду или запросы к виртуальным таблицам регистра бухгалтерии (РегистрБухгалтерии.Хозрасчетный.Остатки), то нагрузка на SQL-сервер возрастает многократно. Регистр бухгалтерии архитектурно сложнее регистров накопления, так как он предназначен для итогов по счетам с учетом субконто, что делает получение остатков по нему более ресурсозатратным.

Решение 1: Кэширование данных в Таблице Значений

Самый эффективный способ убрать задержки при скроллинге — это отказаться от выполнения запросов в событии ПриПолученииДанных. Вместо этого мы можем подготовить данные один раз при открытии формы или по требованию.

Рассмотрим алгоритм реализации кэширования:

  1. Создадим переменную модуля формы тзОстатки.
  2. При открытии формы или при изменении фильтров заполним эту таблицу всеми актуальными остатками.
  3. В событии ПриПолученииДанных будем просто искать значения в этой таблице.

Пример реализации в модуле формы:


Перем тзОстатки;

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

Процедура СправочникСписокПриПолученииДанных(Элемент, ОформленияСтрок)
    Для Каждого Оформление Из ОформленияСтрок Цикл
        СсылкаНаТовар = Оформление.ДанныеСтроки.Ссылка;
        СтрокаОстатка = тзОстатки.Найти(СсылкаНаТовар, "Товар");
        
        Если СтрокаОстатка <> Неопределено Тогда
            Оформление.Ячейки.Остаток.Значение = СтрокаОстатка.Остаток;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

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

Решение 2: Оптимизация запроса внутри события

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

Во-первых, использование НайтиПоКоду внутри процедуры — это критическая ошибка. Каждый такой вызов — это отдельный мини-запрос к БД. Выясним, как сделать правильно: ссылки на склады нужно получить заранее (например, в ПриОткрытии) и передавать в запрос уже готовые объекты или массив.

Во-вторых, обратите внимание на условия в виртуальной таблице. Чем точнее наложен фильтр внутри параметров виртуальной таблицы (в скобках), тем быстрее SQL-сервер вернет результат.

Оптимизированный вариант запроса в ПриПолученииДанных:


// В параметрах виртуальной таблицы передаем только те товары, которые сейчас на экране
МассивНоменклатуры = Новый Массив;
Для Каждого Строка Из ОформленияСтрок Цикл
    МассивНоменклатуры.Добавить(Строка.ДанныеСтроки.Ссылка);
КонецЦикла;

Запрос.Текст = 
    "ВЫБРАТЬ
    |   Остатки.Субконто1 КАК Товар,
    |   СУММА(ВЫБОР КОГДА Остатки.Субконто2 = &Склад1 ТОГДА Остатки.КоличествоОстатокДт ИНАЧЕ 0 КОНЕЦ) КАК Склад1_Остаток
    |ИЗ
    |   РегистрБухгалтерии.Хозрасчетный.Остатки(&Дата, Счет В (&Счета), , Субконто1 В (&МассивНоменклатуры)) КАК Остатки
    |СГРУППИРОВАТЬ ПО
    |   Остатки.Субконто1";

Решение 3: Переход на использование Регистров Накопления

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

Решение 4: Динамические списки и Управляемые формы

Несмотря на то что ваше основное приложение может работать на обычных формах, платформа 8.3 позволяет интегрировать элементы управляемого интерфейса. Мы можем создать форму списка номенклатуры на Управляемых формах и использовать механизм Динамического списка.

Преимущества этого подхода:

Для реализации этого метода достаточно в свойствах конфигурации разрешить использование управляемых форм в обычном приложении и создать новую форму списка. Вызов такой формы из обычного интерфейса происходит стандартным методом ОткрытьФорму().

Решение 5: Использование фоновых заданий и обработки ожидания

Для продвинутых систем можно реализовать асинхронное обновление. Рассмотрим схему:

  1. При открытии формы запускается Фоновое задание, которое собирает остатки в фоновом режиме, не блокируя работу пользователя.
  2. Результат задания помещается во временное хранилище.
  3. На форме работает ПодключитьОбработчикОжидания, который раз в несколько секунд проверяет готовность данных.
  4. Как только данные готовы, форма обновляет внутренний кэш (ТЗ) и перерисовывает список.

Этот метод позволяет пользователю начать работу с формой мгновенно, а актуальные остатки «подтянутся» через секунду-две без фризов интерфейса.

Технический совет по SQL-серверу

Выясним еще одну причину тормозов: база TempDB. При выполнении запросов с использованием временных таблиц (а именно так 1С работает с виртуальными таблицами остатков при передаче массива ссылок) активно задействуется системная база SQL-сервера TempDB. Если она расположена на медленном HDD, никакая оптимизация кода не поможет на 100%. Убедитесь, что TempDB находится на быстром SSD-диске, а количество ее файлов соответствует количеству ядер процессора.

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

← На главную