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

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При работе с объектом ТаблицаЗначений в платформе 1С:Предприятие 7.7 разработчики часто сталкиваются с ограничением штатного метода Свернуть(). Данный метод умеет только группировать строки по заданным колонкам и суммировать числовые ресурсы. Если же в результирующей таблице требуется получить максимальное (или минимальное) значение — например, максимальную дату, последнее заполненное строковое значение или непустую ссылку на элемент справочника при объединении записей, — стандартного функционала становится недостаточно.

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

Способ 1. Использование внешней компоненты 1С++ и класса ИндексированнаяТаблица

Если в конфигурации подключена внешняя компонента 1С++, наиболее удобным и лаконичным решением является объект ИндексированнаяТаблица. Он предоставляет встроенный метод группировки с сохранением подчиненных строк во вложенной таблице потомков тзПотомки.

Разберем по шагам алгоритм с использованием ИндексированнаяТаблица:

  1. Загружаем исходную таблицу значений в объект ИндексированнаяТаблица.
  2. Выполняем группировку по ключевым колонкам.
  3. В цикле обходим сгруппированные строки, выгружаем подчиненные строки группы (тзПотомки) во временную таблицу значений.
  4. Сортируем подчиненную таблицу по убыванию нужной колонки (знак минус в параметрах сортировки), в результате чего строка с максимальным значением оказывается первой.
  5. Получаем первую строку и извлекаем требуемый максимум.

Посмотрим на пример программного кода:


ИТЗ = СоздатьОбъект("ИндексированнаяТаблица");
ТЗП = СоздатьОбъект("ТаблицаЗначений");

// Загружаем данные из исходной ТЗ
ИТЗ.Загрузить(ИсходнаяТЗ);

// Выполняем группировку по ключевому полю
ИТЗ.Группировать("ИнКолонкаПоКоторойГруппируем:КолонкаПоКоторойГруппируем", "", 1);

ИТЗ.ВыбратьСтроки();
Пока ИТЗ.ПолучитьСтроку() = 1 Цикл
    // Выгружаем строки текущей группы во временную ТЗ
    ИТЗ.тзПотомки.Выгрузить(ТЗП);
    
    // Сортируем по убыванию нужного поля для поиска максимума
    ТЗП.Сортировать("КолонкаПоКоторойНуженМаксимум-");
    
    // Первая строка содержит максимальное значение
    ТЗП.ПолучитьСтрокуПоНомеру(1);
    МаксЗначение = ТЗП.КолонкаПоКоторойНуженМаксимум;
    
    // Выполняем дальнейшую обработку или заполнение результирующей таблицы
КонецЦикла;

Способ 2. Однопроходный штатный алгоритм с предварительной сортировкой

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

Разберем логику работы этого метода:

  1. Сортируем исходную таблицу сразу по полям группировки (по возрастанию) и по полю максимума (по убыванию): ТЗ.Сортировать("ПолеГруппировки+,ПолеМаксимума-");.
  2. За счет сортировки по убыванию максимальное значение группы всегда гарантированно находится в самой первой строке группы.
  3. Создаем новую результирующую ТаблицаЗначений с нужной структурой колонок.
  4. Обходим строки исходной таблицы, отслеживая момент смены значения группировочной колонки.
  5. Для первой строки группы фиксируем максимальное значение и инициализируем накопительные суммы. Для последующих строк этой же группы только увеличиваем числовые суммы.
  6. При смене группы (или по завершении цикла) записываем итоговую строку в результирующую таблицу.

Рассмотрим реализацию на примере кода:


// Подготавливаем результирующую таблицу
ТЗ_Рез = СоздатьОбъект("ТаблицаЗначений");
ТЗ_Рез.НоваяКолонка("ID", "Число");
ТЗ_Рез.НоваяКолонка("Сумма1С", "Число");
ТЗ_Рез.НоваяКолонка("СуммаДругая", "Число");
ТЗ_Рез.НоваяКолонка("Юл1С", "Строка");
ТЗ_Рез.НоваяКолонка("ЮлДругая", "Строка");

// Сортируем исходную таблицу: по ключу - по возрастанию, по искомым полям - по убыванию
ИсходнаяТЗ.Сортировать("ID+,Юл1С-,ЮлДругая-");

ТекID = "";
ТекСумма1 = 0;
ТекСумма2 = 0;
ТекЮл1 = "";
ТекЮл2 = "";
ЕстьДанные = 0;

ИсходнаяТЗ.ВыбратьСтроки();
Пока ИсходнаяТЗ.ПолучитьСтроку() = 1 Цикл
    Если ИсходнаяТЗ.ID <> ТекID Тогда
        // Записываем накопленные данные предыдущей группы
        Если ЕстьДанные = 1 Тогда
            ТЗ_Рез.НоваяСтрока();
            ТЗ_Рез.ID = ТекID;
            ТЗ_Рез.Сумма1С = ТекСумма1;
            ТЗ_Рез.СуммаДругая = ТекСумма2;
            ТЗ_Рез.Юл1С = ТекЮл1;
            ТЗ_Рез.ЮлДругая = ТекЮл2;
        КонецЕсли;
        
        // Инициализируем новую группу
        ТекID = ИсходнаяТЗ.ID;
        ТекСумма1 = ИсходнаяТЗ.Сумма1С;
        ТекСумма2 = ИсходнаяТЗ.СуммаДругаяСистема;
        // Так как сортировка была по убыванию, первое значение является максимальным (непустым)
        ТекЮл1 = ИсходнаяТЗ.Юл1С;
        ТекЮл2 = ИсходнаяТЗ.ЮлДругаяСистема;
        ЕстьДанные = 1;
    Иначе
        // Продолжаем накапливать суммы внутри текущей группы
        ТекСумма1 = ТекСумма1 + ИсходнаяТЗ.Сумма1С;
        ТекСумма2 = ТекСумма2 + ИсходнаяТЗ.СуммаДругаяСистема;
    КонецЕсли;
КонецЦикла;

// Сбрасываем последнюю группу в результат
Если ЕстьДанные = 1 Тогда
    ТЗ_Рез.НоваяСтрока();
    ТЗ_Рез.ID = ТекID;
    ТЗ_Рез.Сумма1С = ТекСумма1;
    ТЗ_Рез.СуммаДругая = ТекСумма2;
    ТЗ_Рез.Юл1С = ТекЮл1;
    ТЗ_Рез.ЮлДругая = ТекЮл2;
КонецЕсли;

Способ 3. Вычисление максимумов через промежуточную группировку двумя таблицами

Еще один штатный прием заключается в разделении задачи на суммирование и поиск крайних значений с помощью двух таблиц значений:

  1. Копируем исходную таблицу значений и сворачиваем ее стандартным методом Свернуть("ПоляГруппировки", "СуммируемыеПоля"). Получаем уникальный список групп с посчитанными суммами.
  2. Сортируем исходную таблицу по полям группировки и убыванию поля максимума.
  3. В цикле обходим свернутую таблицу и для каждой группы находим первое совпадение в исходной отсортированной таблице (например, через НайтиЗначение() или позиционирование по ключам).
  4. Переносим найденный максимум в результирующую таблицу.

Особенности работы со ссылочными типами данных

Если требуется находить максимум среди объектов ссылочного типа (справочники, документы) или выбирать между пустой и заполненной ссылкой, следует учитывать нюансы сравнения в платформе 1С 7.7:

  1. Проверка на пустое значение: если цель состоит в получении заполненного значения (выбрать из пары «пусто / ссылка»), при обходе строк достаточно использовать функцию ПустоеЗначение(). Если сохраненное значение пустое, а текущее заполнено — перезаписываем его.
  2. Использование внутреннего представления: для строгого сравнения или упорядочивания ссылок можно использовать строковое представление через функцию ЗначениеВСтрокуВнутр(). Полученную уникальную строку или идентификатор объекта помещают во вспомогательную колонку, по которой затем выполняется стандартная сортировка или сравнение. Для восстановления объекта обратно применяется функция ЗначениеИзСтрокиВнутр().

Таким образом, для простых задач сопоставления достаточно однопроходного алгоритма с предварительной сортировкой (Способ 2), а при наличии компоненты 1С++ наиболее масштабируемым и быстрым решением будет использование ИндексированнаяТаблица или прямых SQL-запросов к таблице в памяти.

← На главную