При работе с таблицами значений в 1С разработчики часто сталкиваются с необходимостью сгруппировать записи, содержащие не только числовые показатели, но и строковые символы (например, обозначения отработанного времени или видов дней в табеле: "О", "Я", "В"). Попытка использовать стандартный метод Свернуть() для таких колонок часто приводит к тому, что все символы сбрасываются в нулевые значения.
Рассмотрим подробно, почему это происходит и как правильно решить эту задачу разными способами.
Проанализируем особенности работы платформы 1С при выполнении метода ТаблицаЗначений.Свернуть(). Второй параметр метода предназначен исключительно для числового суммирования. Платформа работает по следующим правилам:
Число. Если в строке содержится нечисловой символ (например, буква "О"), результат преобразования дает 0.0, который затем приводится к значению по умолчанию для типа колонки.Таким образом, метод Свернуть() не предназначен для конкатенации (склеивания) или текстового объединения строк. Чтобы сохранить строковые символы при группировке, необходимо задействовать альтернативные подходы.
Разберем вариант, когда группировка выполняется через язык запросов с применением агрегатных функций (например, МИНИМУМ или МАКСИМУМ). Если колонка таблицы значений создана без ограничения длины строки, при попытке передать ее в запрос возникнет ошибка вида: Неверные параметры "МИНИМУМ".
Чтобы избежать этой ошибки и успешно сгруппировать данные в запросе, разберем по шагам правильную подготовку таблицы и сам текст запроса.
// Правильное создание колонок с указанием квалификаторов строки
КвалификаторыСтр = Новый КвалификаторыСтроки(300);
МассивТиповСтрока = Новый Массив;
МассивТиповСтрока.Добавить(Тип("Строка"));
ОписаниеТипаСтрока = Новый ОписаниеТипов(МассивТиповСтрока, , КвалификаторыСтр);
Таб.Колонки.Добавить("День1", ОписаниеТипаСтрока);
Таб.Колонки.Добавить("День2", ОписаниеТипаСтрока);
ВЫРАЗИТЬ в самом запросе.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВнешняяТаблица.ИНН КАК ИНН,
| ВнешняяТаблица.Сотрудник КАК Сотрудник,
| ВнешняяТаблица.Должность КАК Должность,
| ВнешняяТаблица.День1 КАК День1,
| ВнешняяТаблица.День2 КАК День2
|ПОМЕСТИТЬ ВнТаб
|ИЗ
| &ТаблицаИсточник КАК ВнешняяТаблица
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВнТаб.ИНН КАК ИНН,
| ВнТаб.Сотрудник КАК Сотрудник,
| ВнТаб.Должность КАК Должность,
| МИНИМУМ(ВЫРАЗИТЬ(ВнТаб.День1 КАК Строка(300))) КАК День1,
| МИНИМУМ(ВЫРАЗИТЬ(ВнТаб.День2 КАК Строка(300))) КАК День2
|ИЗ
| ВнТаб КАК ВнТаб
|СГРУППИРОВАТЬ ПО
| ВнТаб.ИНН,
| ВнТаб.Сотрудник,
| ВнТаб.Должность";
Запрос.УстановитьПараметр("ТаблицаИсточник", Таб);
РезультатТаблица = Запрос.Выполнить().Выгрузить();
Важное примечание: использование МИНИМУМ или МАКСИМУМ выбирает значение по алфавитному порядку. Это подходит для случаев, когда в одной из объединяемых строк день заполнен (содержит букву), а в другой — пуст.
Если логика объединения требует более сложной обработки (например, заполнение пустых значений из второй строки или физическая склейка строк), оптимальным решением является однопроходный алгоритм на языке 1С с применением объекта Соответствие.
Рассмотрим пример реализации такого алгоритма:
// Создаем результирующую таблицу с аналогичной структурой
РезультирующаяТаб = Таб.СкопироватьКолонки();
КартаЗаписей = Новый Соответствие;
Для Каждого ИсходнаяСтрока Из Таб Цикл
// Формируем уникальный ключ группировки
КлючГруппировки = СтрШаблон("%1_%2_%3", ИсходнаяСтрока.ИНН, ИсходнаяСтрока.Сотрудник, ИсходнаяСтрока.Должность);
НайденнаяСтрока = КартаЗаписей.Получить(КлючГруппировки);
Если НайденнаяСтрока = Неопределено Тогда
// Если запись встречается впервые, добавляем ее в новую таблицу
НайденнаяСтрока = РезультирующаяТаб.Добавить();
ЗаполнитьЗначенияСвойств(НайденнаяСтрока, ИсходнаяСтрока);
КартаЗаписей.Вставить(КлючГруппировки, НайденнаяСтрока);
Иначе
// Если запись уже есть, переносим заполненные символы
Для Инд = 1 По 7 Цикл
ИмяКолонки = "День" + Строка(Инд);
Если Не ЗначениеЗаполнено(НайденнаяСтрока[ИмяКолонки])
И ЗначениеЗаполнено(ИсходнаяСтрока[ИмяКолонки]) Тогда
НайденнаяСтрока[ИмяКолонки] = ИсходнаяСтрока[ИмяКолонки];
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
Данный подход обеспечивает высокую скорость работы ($O(N)$), так как поиск через Соответствие выполняется мгновенно, а строковые данные не подвергаются нежелательным преобразованиям типов.