Как свернуть две колонки таблицы значений в одну с суммированием в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки в среде «1С:Предприятие» программисты часто сталкиваются с задачей агрегации данных. Типичный сценарий: имеется таблица значений, в которой числовые показатели разнесены по двум разным колонкам (например, приход и расход, или положительные и отрицательные разницы). Нам необходимо свернуть эту таблицу по ключевым полям (например, по номенклатуре или складу), но при этом объединить эти две колонки в одну результирующую, рассчитав итоговое сальдо (плюс минус минус).

Стандартный метод таблицы значений Свернуть() позволяет суммировать только значения внутри одной и той же колонки. Напрямую сложить или вычесть колонки друг из друга в момент свертки этот метод не умеет. В данной статье мы подробно разберем три основных способа решения этой задачи: классический обход в цикле с добавлением вспомогательной колонки, использование механизма запросов и применение Системы компоновки данных (СКД). Мы проанализируем особенности каждого подхода, оценим их производительность и выявим подводные камни.

Способ 1. Обход в цикле с добавлением вспомогательной колонки (Оптимальный для малых и средних ТЗ)

Этот способ является наиболее очевидным и простым в реализации. Суть его заключается в том, что мы сначала подготавливаем таблицу значений: добавляем в нее новую колонку для итоговой суммы, рассчитываем ее значение для каждой строки в цикле (вычитая или складывая исходные колонки), а затем вызываем стандартный метод Свернуть().

Рассмотрим по шагам, как правильно реализовать этот алгоритм, избежав типичных ошибок производительности.

Важнейший нюанс: обязательная типизация новой колонки.

При добавлении вспомогательной колонки через метод Колонки.Добавить() крайне важно явно передавать третьим параметром объект ОписаниеТипов. Если добавить колонку без указания типа, платформа будет выделять память динамически под каждую строку, присваивая ей тип по умолчанию Неопределено. Это критически снижает общую производительность при последующем обходе строк и вызове метода Свернуть() на таблицах, содержащих более нескольких тысяч строк.

Автоматическое удаление исходных колонок:

При вызове метода ТЗ.Свернуть("КолонкиГруппировки", "НоваяОбщаяКолонка") нет необходимости вручную удалять старые колонки (например, «СуммаПлюс» и «СуммаМинус»). Метод Свернуть() автоматически удаляет из таблицы значений все колонки, которые не были перечислены в параметрах группировки или суммирования. Это делает код чище и избавляет от лишних операций.

Проанализируем пример правильного кода:


// 1. Создаем описание типа "Число" для новой колонки
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
КвалификаторыЧ = Новый КвалификаторыЧисла(15, 2);
ОписаниеЧисла = Новый ОписаниеТипов(МассивТипов, , , КвалификаторыЧ);

// 2. Добавляем строго типизированную колонку в исходную таблицу значений
ТаблицаДанных.Колонки.Добавить("СуммаИтого", ОписаниеЧисла);

// 3. Заполняем новую колонку в цикле, вычисляя разницу
Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
    СтрокаДанных.СуммаИтого = СтрокаДанных.СуммаПлюс - СтрокаДанных.СуммаМинус;
КонецЦикла;

// 4. Сворачиваем таблицу по ключевым полям. 
// Колонки "СуммаПлюс" и "СуммаМинус" будут удалены автоматически.
ТаблицаДанных.Свернуть("Номенклатура, Склад", "СуммаИтого");

Данный метод работает чрезвычайно быстро на небольших объемах данных (до 5 000 – 10 000 строк). Он прост в отладке и не требует обращения к СУБД.

Способ 2. Обработка таблицы значений через Запрос (Для больших объемов данных)

Когда количество строк в таблице превышает десятки тысяч, обход в цикле на встроенном языке 1С может начать уступать по производительности механизмам СУБД. В этом случае эффективнее передать таблицу значений в запрос, произвести там все математические операции и сгруппировать результат.

Выясним технические особенности выполнения этой операции:

  1. Проблема типизации исходной ТЗ: Чтобы передать таблицу значений в запрос в качестве параметра (через конструкцию &ВнешняяТЗ), абсолютно все её колонки, которые будут использоваться в запросе, должны быть жестко типизированы при создании таблицы. Если хотя бы у одной колонки тип будет составным (содержащим Неопределено) или вообще не задан, платформа выбросит исключение на этапе выполнения запроса.
  2. Фактор файлового и клиент-серверного вариантов работы:
    • В клиент-серверном варианте (SQL): Запрос практически всегда работает быстрее цикла на объемах более 10 000 строк. Передача ТЗ в запрос заставляет платформу создать временную таблицу в СУБД, а SQL-сервер оптимизирует группировку и математические операции на уровне своего ядра гораздо эффективнее.
    • В файловом варианте: При передаче ТЗ в запрос временная таблица создается в локальной базе данных на диске. Это может вызывать дисковые задержки ввода-вывода. Поэтому на больших объемах в файловой базе метод с циклом иногда может показать себя производительнее.

Посмотрим на пример реализации этого метода:


// Создаем запрос для обработки таблицы значений
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВремТЗ.Номенклатура КАК Номенклатура,
    |	ВремТЗ.Склад КАК Склад,
    |	ВремТЗ.СуммаПлюс - ВремТЗ.СуммаМинус КАК СуммаИтого
    |ПОМЕСТИТЬ ВтДанные
    |ИЗ
    |	&ВнешняяТЗ КАК ВремТЗ
    |;
    |
    |ВЫБРАТЬ
    |	ВтДанные.Номенклатура КАК Номенклатура,
    |	ВтДанные.Склад КАК Склад,
    |	СУММА(ВтДанные.СуммаИтого) КАК СуммаИтого
    |ИЗ
    |	ВтДанные КАК ВтДанные
    |
    |СГРУППИРОВАТЬ ПО
    |	ВтДанные.Номенклатура,
    |	ВтДанные.Склад";

// Устанавливаем таблицу в качестве параметра. 
// Помним: колонки Номенклатура, Склад, СуммаПлюс, СуммаМинус должны быть жестко типизированы!
Запрос.УстановитьПараметр("ВнешняяТЗ", ТаблицаДанных);

// Выполняем запрос и выгружаем результат в новую свернутую таблицу
ТаблицаСвернутая = Запрос.Выполнить().Выгрузить();

Способ 3. Использование Системы компоновки данных (СКД)

Система компоновки данных — крайне мощный инструмент, но применять его нужно обдуманно. Рассмотрим ситуации, в которых использование СКД оправдано, а в каких — избыточно.

Важный нюанс: Местонахождение таблицы значений и Тонкий клиент

При проектировании кода важно помнить о распределении контекста между клиентом и сервером в управляемом приложении:

В управляемых формах (Тонкий клиент, Веб-клиент) объект типа ТаблицаЗначений физически отсутствует на стороне клиента. Вместо него на клиенте данные хранятся в специальном объекте-оболочке ДанныеФормыКоллекция (или ДанныеФормыДерево). Этот объект имеет существенно ограниченный набор методов — у него нет встроенного метода Свернуть().

Поэтому, если ваши данные находятся в таблице на форме (на клиенте), вам в любом случае придется передать их на сервер (через вызов серверной процедуры), преобразовать коллекцию в полноценную таблицу значений, выполнить свертку (через цикл или запрос) и вернуть результат обратно на клиент.

Проанализируем пример схемы передачи данных:


&НаКлиенте
Процедура ОбработатьТаблицуНаКлиенте()
    // На клиенте метод Свернуть() вызвать нельзя. Передаем данные на сервер.
    СвернутьТаблицуНаСервере();
КонецПроцедуры

&НаСервере
Процедура СвернутьТаблицуНаСервере()
    // 1. Конвертируем данные формы в физическую ТаблицуЗначений
    ТаблицаОбъект = ДанныеФормыВЗначение(Объект.ИмяТабличнойЧасти, Тип("ТаблицаЗначений"));
    
    // 2. Добавляем колонку с типом
    МассивТипов = Новый Массив;
    МассивТипов.Добавить(Тип("Число"));
    ОписаниеЧисла = Новый ОписаниеТипов(МассивТипов, , , Новый КвалификаторыЧисла(15, 2));
    ТаблицаОбъект.Колонки.Добавить("СуммаИтого", ОписаниеЧисла);
    
    // 3. Вычисляем результат в цикле
    Для Каждого Строка Из ТаблицаОбъект Цикл
        Строка.СуммаИтого = Строка.СуммаПлюс - Строка.СуммаМинус;
    КонецЦикла;
    
    // 4. Сворачиваем данные
    ТаблицаОбъект.Sвернуть("Номенклатура", "СуммаИтого");
    
    // 5. Возвращаем результат обратно в реквизит формы
    ЗначениеВДанныеФормы(ТаблицаОбъект, Объект.ИмяТабличнойЧасти);
КонецПроцедуры

Резюме

Мы подробно разобрали задачу объединения двух числовых колонок в одну при свертке таблицы значений. Для большинства повседневных задач оптимальным выбором будет Способ 1 (Цикл + Свернуть) с обязательным указанием типа добавляемой колонки — это просто, быстро и надежно. Если же вам приходится оперировать большими массивами данных (от 10 000 строк) в клиент-серверном варианте, имеет смысл использовать Способ 2 (Запрос). Использовать СКД стоит только тогда, когда итоговые свернутые данные предназначаются для непосредственного вывода пользователю в отчет.

← На главную