При разработке на платформе 1С:Предприятие программисты часто сталкиваются с необходимостью обработки "широких" таблиц значений. Классический пример — сетка Табеля учета рабочего времени, где строки содержат сотрудников, а колонки (которых может быть больше 30, а иногда и за 200) соответствуют дням месяца или видам начислений. Ручной перебор каждой колонки в коде неэффективен, превращает разработку в рутину и приводит к созданию огромного, трудноподдерживаемого кода. Например, если нам нужно очистить все ячейки со значением "Я" и заменить "ЯН" на "Я", писать сотни условий для каждой колонки вручную — худшее решение.
В этой статье мы подробно разберем четыре профессиональных метода решения этой задачи: от простых и безопасных до максимально быстрых и архитектурно правильных.
Самый очевидный страх начинающего разработчика — необходимость писать сотни строк кода для каждой колонки индивидуально. Однако во встроенном языке 1С коллекция колонок таблицы значений доступна для динамического обхода через свойство Колонки. Это позволяет написать один универсальный вложенный цикл, который автоматически обработает таблицу любой ширины.
Рассмотрим детально, как реализовать этот алгоритм на практике:
// Предположим, что ТЗ — это наша исходная таблица значений (например, выгрузка табеля)
Для Каждого СтрокаТЗ Из ТЗ Цикл
Для Каждого Колонка Из ТЗ.Колонки Цикл
// Шаг 1. Исключаем служебные колонки, которые не содержат виды времени
Если Колонка.Имя = "ФИО" Или Колонка.Имя = "Сотрудник" Тогда
Продолжить;
КонецЕсли;
// Шаг 2. Получаем значение ячейки по имени колонки (используем квадратные скобки)
ТекущееЗначение = СтрокаТЗ[Колонка.Имя];
// Шаг 3. Анализируем значение и производим замену
Если ТекущееЗначение = "Я" Тогда
СтрокаТЗ[Колонка.Имя] = ""; // Полностью очищаем ячейку
ИначеЕсли ТекущееЗначение = "ЯН" Тогда
СтрокаТЗ[Колонка.Имя] = "Я"; // Заменяем ЯН на Я
КонецЕсли;
КонецЦикла;
КонецЦикла;
Разберем преимущества и недостатки данного подхода:
На форумах часто предлагают использовать функцию СтрЗаменить. Напрямую применить строковую замену к объекту типа ТаблицаЗначений нельзя. Однако мы можем применить "обходной маневр": быстро перевести таблицу в текстовый формат (XML или JSON), выполнить глобальную замену подстрок в полученном тексте, а затем восстановить таблицу значений обратно.
Этот метод показывает феноменальную скорость на больших объемах данных, так как строковые операции в платформе 1С выполняются на низком уровне (C++), минуя интерпретатор встроенного языка.
Посмотрим на пример реализации через XML-сериализацию:
// Шаг 1. Сериализуем Таблицу Значений в текстовую строку XML
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
Сериализатор = Новый СериализаторXDTO(ФабрикаXDTO);
Сериализатор.ЗаписатьXML(ЗаписьXML, ТЗ);
СтрокаXML = ЗаписьXML.Закрыть();
// Шаг 2. Выполняем точечную замену данных внутри XML-тегов
// Важно обрамлять искомые значения угловыми скобками тегов, чтобы не повредить служебную разметку
СтрокаXML = СтрЗаменить(СтрокаXML, ">Я<", "><"); // Очищаем значение "Я"
СтрокаXML = СтрЗаменить(СтрокаXML, ">ЯН<", ">Я<"); // Заменяем "ЯН" на "Я"
// Шаг 3. Десериализуем XML-строку обратно в объект ТаблицаЗначений
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаXML);
ТЗ = Сериализатор.ПрочитатьXML(ЧтениеXML);
Критически важный момент: Этот способ требует предельной осторожности. Если у вас в таблице есть сотрудник с фамилией "Ян" или организация "Январь", обычная замена строки может случайно повредить эти данные. Чтобы минимизировать риски, всегда используйте точное сопоставление вместе с закрывающими и открывающими тегами XML (например, >ЯН<), но даже это не гарантирует 100% безопасности, если строковые значения совпадают с кодами видов времени.
Если обработка данных происходит на сервере, отличным решением будет использование механизма запросов. Мы можем передать таблицу значений в запрос как параметр, но сам текст запроса собрать динамически в цикле по колонкам. Это позволит выполнить всю тяжелую работу по замене значений на уровне СУБД или оптимизированного движка временных таблиц 1С.
Рассмотрим, как это реализовать:
ТекстВыбораКолонок = "";
// Динамически формируем поля выборки для каждого поля таблицы
Для Каждого Колонка Из ТЗ.Колонки Цикл
Если Колонка.Имя = "ФИО" Тогда
ТекстВыбораКолонок = ТекстВыбораКолонок + "ТЗ.ФИО КАК ФИО, " + Символы.ПС;
Иначе
// Строим условную конструкцию ВЫБОР КОГДА для рабочих колонок
ТекстВыбораКолонок = ТекстВыбораКолонок +
"ВЫБОР " +
" КОГДА ТЗ." + Колонка.Имя + " = ""Я"" ТОГДА """" " +
" КОГДА ТЗ." + Колонка.Имя + " = ""ЯН"" ТОГДА ""Я"" " +
" ИНАЧЕ ТЗ." + Колонка.Имя + " " +
"КОНЕЦ КАК " + Колонка.Имя + ", " + Символы.ПС;
КонецЕсли;
КонецЦикла;
// Отрезаем лишнюю запятую и перенос строки в конце списка полей
ТекстВыбораКолонок = Лев(ТекстВыбораКолонок, СтрДлина(ТекстВыбораКолонок) - 3);
// Формируем и выполняем запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ " + Символы.ПС +
ТекстВыбораКолонок + Символы.ПС +
"ПОМЕСТИТЬ ВременнаяТаблица " + Символы.ПС +
"ИЗ &ВходнаяТЗ КАК ТЗ; " + Символы.ПС +
" " +
"ВЫБРАТЬ * ИЗ ВременнаяТаблица";
Запрос.УстановитьПараметр("ВходнаяТЗ", ТЗ);
ТЗ = Запрос.Выполнить().Выгрузить();
Этот метод сочетает в себе высокую скорость выполнения операций в СУБД и полную типизацию данных. Риск случайно повредить структуру таблицы значений или строковые данные (как в случае с XML-хаком) здесь полностью отсутствует.
Наличие в таблице 250 колонок — это классический признак денормализации данных (так называемая "широкая таблица"). С точки зрения архитектуры баз данных и стандартов разработки 1С, гораздо правильнее работать с "узкой" таблицей. Вместо структуры ФИО | День1 | День2 | ... | День31 правильная структура должна выглядеть так: Сотрудник | НомерДня | ВидВремени | Значение.
Если мы приведем таблицу к нормализованному виду, задача замены значений решится невероятно просто. Проанализируем пример работы с нормализованной таблицей:
// Структура нормализованной таблицы значений:
// Колонки: Сотрудник, День, ВидВремени, Часы
Для Каждого Строка Из НормализованнаяТЗ Цикл
Если Строка.ВидВремени = "Я" Тогда
Строка.ВидВремени = ""; // Очищаем значение
ИначеЕсли Строка.ВидВремени = "ЯН" Тогда
Строка.ВидВремени = "Я"; // Производим замену
КонецЕсли;
КонецЦикла;
Преимущества нормализации очевидны:
Если для вывода пользователю на форму или для печати вам все же требуется именно "широкая" таблица (в виде привычной сетки дней), выполняйте трансформацию данных в самый последний момент — при выводе на форму (например, используя программное добавление колонок на форму или компоновку данных через СКД), но всю бизнес-логику и расчеты проводите строго над нормализованной "узкой" таблицей.