При разработке интерфейсов на платформе «1С:Предприятие» перед программистом часто возникает задача оперативного подсчета данных в табличной части. Классический пример — вывод количества заполненных строк на форму (например, для отображения прогресса сопоставления или заполнения спецификации). Самый очевидный путь, который выбирают многие начинающие разработчики, — это обход таблицы в цикле с проверкой каждого элемента. Однако при увеличении объема данных или при вызове такого кода из интерактивных событий формы система начинает заметно зависать.
В этой статье мы подробно разберем, почему циклы в интерфейсных механизмах — это плохая практика, и рассмотрим три эффективных способа подсчета заполненных строк без использования ресурсоемких обходов, работающих со скоростью платформенного кода.
Проанализируем типичную ошибку. Разработчик пытается обновить индикатор на форме прямо во время отрисовки таблицы, используя событие ПриВыводеСтроки:
Процедура ИндикацияСопоставление(Товары)
ИндикаторСоп = ЭлементыФормы.Индикатор1;
ИндикаторСоп.Значение = 0;
ИндикаторСоп.МаксимальноеЗначение = Товары.Количество();
КоличествоЗаполненыхСтрок = 0;
Для Каждого СтрокаТ из Товары Цикл
Если ЗначениеЗаполнено(СтрокаТ.Номенклатура) Тогда
КоличествоЗаполненыхСтрок = КоличествоЗаполненыхСтрок + 1;
КонецЕсли;
КонецЦикла;
ИндикаторСоп.Значение = КоличествоЗаполненыхСтрок;
КонецПроцедуры
Сама по себе функция ЗначениеЗаполнено работает мгновенно, так как она реализована на низком уровне платформы. Проблема кроется в месте вызова. Событие обычных форм ПриВыводеСтроки вызывается платформой непрерывно: при любом скроллинге, изменении размеров окна, активизации строк или просто движении мыши по табличному полю. Запуск полного обхода таблицы при выводе каждой отдельной строки создает колоссальную нагрузку на процессор и приводит к лагам интерфейса.
Важное правило: никогда не выполняйте расчеты по всей таблице и не изменяйте элементы формы внутри событий визуализации строк (ПриВыводеСтроки). Это нарушает логику работы платформы и зацикливает интерфейсный поток.
---
Если нам нужно быстро получить количество строк без явного написания цикла, мы можем использовать встроенный платформенный метод НайтиСтроки(). Этот метод реализован на уровне платформы и работает значительно быстрее, чем ручной перебор элементов на встроенном языке.
Идея проста: мы находим количество пустых строк и вычитаем его из общего количества строк в таблице. Разберем этот пример по шагам:
// Шаг 1. Формируем структуру отбора для поиска пустых значений
Отбор = Новый Структура("Номенклатура", Справочники.Номенклатура.ПустаяСсылка());
// Шаг 2. Находим все пустые строки
КоличествоПустыхСтрок = Товары.НайтиСтроки(Отбор).Количество();
// Шаг 3. Вычисляем количество заполненных строк
КоличествоЗаполненныхСтрок = Товары.Количество() - КоличествоПустыхСтрок;
Внимание, ловушка составных типов данных! Данный метод безупречно работает только тогда, когда колонка таблицы строго типизирована (например, имеет единственный тип СправочникСсылка.Номенклатура). Если колонка имеет составной тип данных, пустое значение может быть представлено как Неопределено, пустая строка или пустая ссылка другого типа. В таком случае простой отбор по пустой ссылке конкретного справочника не найдет все пустые строки, и результат будет некорректным.
---
Для универсального решения, которое корректно обрабатывает любые типы данных (включая составные) и не зависит от пустых ссылок конкретных справочников, идеально подходит использование механизма запросов. Мы можем передать табличную часть в качестве параметра запроса и выполнить подсчет на уровне СУБД или временных таблиц в памяти.
Посмотрим на практический пример реализации такого запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТаблицаДанных.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТ_ТаблицаДанных
|ИЗ
| &ТаблицаДанных КАК ТаблицаДанных
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| КОЛИЧЕСТВО(ВТ_ТаблицаДанных.Номенклатура) КАК КоличествоЗаполненных
|ИЗ
| ВТ_ТаблицаДанных КАК ВТ_ТаблицаДанных
|ГДЕ
| НЕ (ВТ_ТаблицаДанных.Номенклатура ЕСТЬ NULL
| ИЛИ ВТ_ТаблицаДанных.Номенклатура = НЕОПРЕДЕЛЕНО
| ИЛИ ВТ_ТаблицаДанных.Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка))";
Запрос.УстановитьПараметр("ТаблицаДанных", Товары);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
КоличествоЗаполненыхСтрок = Выборка.КоличествоЗаполненных;
Иначе
КоличествоЗаполненыхСтрок = 0;
EndЕсли;
Этот способ является максимально надежным. Запрос учитывает все возможные варианты «пустоты» поля (NULL, Неопределено, пустая ссылка) и мгновенно возвращает точный результат даже для очень больших таблиц.
---
Существует элегантное архитектурное решение, которое позволяет полностью отказаться от выполнения запросов и сложных поисков в момент обращения к данным. Этот прием часто используется опытными разработчиками 1С для оптимизации производительности интерфейса.
Суть метода заключается в делегировании расчетов платформенному методу Итог(), который работает со скоростью скомпилированного C++ кода. Разберем алгоритм по шагам:
Заполнено (ее можно не выводить на форму пользователю).1, если ключевое поле заполнено, и 0, если оно пустое.ПриИзменении для элемента управления, связанного с колонкой Номенклатура, оперативно обновляем значение служебной колонки для текущей строки:
Процедура ТоварыНоменклатураПриИзменении(Элемент)
СтрокаТЧ = ЭлементыФормы.Товары.ТекущаяСтрока;
// Проверяем заполненность интерактивно измененного поля
Если ЗначениеЗаполнено(СтрокаТЧ.Номенклатура) Тогда
СтрокаТЧ.Заполнено = 1;
Иначе
СтрокаТЧ.Заполнено = 0;
КонецЕсли;
// Мгновенно обновляем значение индикатора на форме без циклов и запросов
ЭлементыФормы.Индикатор1.Значение = Товары.Итог("Заполнено");
КонецПроцедуры
Теперь в любой момент времени, когда нам потребуется узнать точное количество заполненных строк, мы можем получить его моментально одной строчкой кода:
КоличествоЗаполненных = Товары.Итог("Заполнено");
Метод Итог() оптимизирован на уровне ядра платформы и мгновенно суммирует данные по колонке, даже если таблица содержит десятки тысяч строк.
---
Чтобы избежать проблем с производительностью интерфейса, важно правильно распределять логику расчетов по событиям формы:
ПриИзменении конкретных ячеек, как показано в Способе 3.ПриВыводеСтроки в пользу события ПриПолученииДанных. Оно передает управление пакетом (массивом оформлений строк), что позволяет получить данные для всех видимых на экране строк за один быстрый проход или один запрос.---
Если вы переводите свои решения на управляемое приложение или разрабатываете новые конфигурации на Управляемых формах, данная задача значительно упрощается благодаря декларативным механизмам платформы:
Выбирайте наиболее подходящий под ваши задачи метод, избегайте тяжелых циклов в интерфейсных событиях, и ваши формы будут работать быстро и плавно!