В процессе разработки на платформе 1С часто возникает задача анализа данных, представленных в виде Таблицы Значений (ТЗ), где колонки соответствуют определенным периодам, например, дням месяца. Рассмотрим ситуацию, когда у нас есть таблица с 31 колонкой (дни), и нам необходимо вычислить, сколько ячеек заполнены данными в конкретном интервале (например, с 5-го по 15-й день).
Разберем по шагам различные подходы к решению этой задачи: от простого перебора по индексам до более гибких методов с использованием динамических имен колонок и оптимизацией производительности.
Если структура вашей таблицы жестко зафиксирована и вы точно знаете порядковые номера колонок, самый простой способ — использовать метод Получить() для объекта СтрокаТаблицыЗначений. Проанализируем, как организовать такой цикл:
// Задаем границы диапазона индексов колонок
Начало = 5; // Индекс 5 соответствует 6-й колонке (индексация с 0)
Конец = 15;
ОбщееКоличествоЗаполненных = 0;
Для Каждого СтрокаТЗ Из ТаблицаЗначений Цикл
Для Сч = Начало По Конец Цикл
// Получаем значение ячейки по индексу
ЗначениеЯчейки = СтрокаТЗ.Получить(Сч);
// Проверяем, заполнено ли значение
Если ЗначениеЗаполнено(ЗначениеЯчейки) Тогда
ОбщееКоличествоЗаполненных = ОбщееКоличествоЗаполненных + 1;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Сообщить("Всего заполнено ячеек в диапазоне: " + ОбщееКоличествоЗаполненных);
Важное замечание: Индексация колонок в 1С начинается с нуля. Если ваша таблица содержит только 31 колонку дней (без дополнительных колонок типа "Номенклатура" или "Сотрудник" в начале), то 5-й индекс будет соответствовать 6-му числу месяца.
Работа по индексам считается рискованной, так как при программном изменении состава колонок (например, добавлении служебного поля в начало ТЗ) индексы сместятся, и логика расчетов нарушится. Рассмотрим более надежный метод — обращение по имени. Выясним, как это реализовать, если имена колонок сформированы по шаблону, например, "День_1", "День_2" и так далее.
НачальныйДень = 5;
КонечныйДень = 15;
ИтогЗаполненных = 0;
Для Каждого ТекущаяСтрока Из ТаблицаЗначений Цикл
Для НомерДня = НачальныйДень По КонечныйДень Цикл
// Формируем имя колонки программно
ИмяКолонки = "День_" + Формат(НомерДня, "ЧГ=0");
// Проверяем наличие колонки в таблице для исключения ошибок
Если ТаблицаЗначений.Колонки.Найти(ИмяКолонки) <> Неопределено Тогда
Если ЗначениеЗаполнено(ТекущаяСтрока[ИмяКолонки]) Тогда
ИтогЗаполненных = ИтогЗаполненных + 1;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Обращение к коллекции Колонки.Найти() внутри вложенного цикла может замедлить работу на больших таблицах (десятки тысяч строк). Проанализируем ситуацию: состав колонок не меняется в процессе обхода строк. Следовательно, мы можем заранее подготовить массив имен колонок, которые входят в нужный нам диапазон.
Рассмотрим алгоритм оптимизации:
// 1. Подготовка списка колонок
МассивИмен = Новый Массив;
Для НомерДня = 5 По 15 Цикл
МассивИмен.Добавить("День_" + Формат(НомерДня, "ЧГ=0"));
КонецЦикла;
// 2. Основной расчет
Всего = 0;
Для Каждого Стр Из ТаблицаЗначений Цикл
Для Каждого ИмяКол Из МассивИмен Цикл
Если ЗначениеЗаполнено(Стр[ИмяКол]) Тогда
Всего = Всего + 1;
КонецЕсли;
КонецЦикла;
КонецЦикла;
ЗначениеЗаполнено()Необходимо учитывать особенности работы функции ЗначениеЗаполнено() при анализе ячеек. Посмотрим, как она ведет себя с разными типами данных:
Ложь, если в ячейке стоит 0. Если для вашей задачи "0" — это тоже заполненная ячейка (например, нулевой остаток), данная функция вам не подойдет. В этом случае используйте проверку Стр[ИмяКол] <> Неопределено.Истина, так как и Истина, и Ложь являются значениями.Ложь для пустых строк.Ложь для пустых ссылок.Итог()Если ваша задача сводится не к проверке "заполнено/не заполнено", а к подсчету суммы значений (например, отработанных часов), 1С предоставляет встроенный высокопроизводительный метод Итог(). Выясним причину, почему он предпочтительнее ручного обхода строк:
Метод ТаблицаЗначений.Итог("ИмяКолонки") реализован на уровне платформы и работает значительно быстрее, чем цикл Для Каждого. Если в ваших колонках хранятся числа 1 (заполнено) и 0 (пусто), код будет выглядеть так:
ОбщийИтог = 0;
Для НомерДня = 5 По 15 Цикл
ИмяКолонки = "День_" + НомерДня;
ОбщийИтог = ОбщийИтог + ТаблицаЗначений.Итог(ИмяКолонки);
КонецЦикла;
Проанализируем ситуацию, когда строк в таблице очень много (сотни тысяч). В этом случае даже оптимизированный цикл в коде может работать долго. Наиболее эффективным решением будет передача Таблицы Значений в запрос.
Разберем этот механизм:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ;
|ВЫБРАТЬ СУММА(День_5 + День_6 + ... + День_15) КАК Итого ИЗ ВТ_Данные";
Запрос.УстановитьПараметр("ТЗ", ТаблицаЗначений);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Подводя итог, выбор метода зависит от конкретной архитектуры вашего решения. Для быстрой разработки при небольших объемах данных идеально подходит Способ 1 или Способ 2. Если же важна надежность и производительность — стоит смотреть в сторону предварительного кэширования имен колонок или использования методов Итог() и запросов.