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