Метод Свернуть() является одним из самых часто используемых инструментов при работе с объектом ТаблицаЗначений. Казалось бы, его логика проста: сгруппировать одинаковые строки и просуммировать значения. Однако на практике разработчики, особенно начинающие, часто сталкиваются со "странным" поведением метода: дубликаты не исчезают, а суммы считаются некорректно. Разберем детально, как работает этот механизм, какие ошибки при его вызове наиболее типичны и на что нужно обратить внимание, чтобы результат всегда был предсказуемым.
Проанализируем первую типичную ситуацию. Разработчик вызывает метод следующим образом:
тзМояТаблица.Свернуть("ИмяКолонки1, ИмяКолонки2, ИмяКолонки3, ИмяКолонки4", "ИмяКолонки5");
Ожидание: в ИмяКолонки1 должны остаться только уникальные значения.
Реальность: в таблице по-прежнему видны строки, где ИмяКолонки1 повторяется.
Выясним причину такого поведения. Метод Свернуть() воспринимает первый параметр (колонки группировки) не как список по отдельности, а как составной ключ уникальности. Это можно сравнить с измерениями в регистре накопления или условием СГРУППИРОВАТЬ ПО в языке запросов SQL/1С.
Рассмотрим на примере. Если у нас есть три строки:
Если мы выполним Свернуть("Организация, Город, Склад", "Сумма"), ни одна строка не схлопнется. Почему? Потому что сочетания значений во всех трех колонках группировки разные. Для системы "Сбербанк + Москва" и "Сбербанк + Питер" — это разные сущности. Чтобы получить уникальный список организаций, в первом параметре должна остаться только одна колонка:
тзМояТаблица.Свернуть("ИмяКолонки1", "ИмяКолонки5");
В этом случае система оставит только уникальные значения из первой колонки, а значения из пятой колонки просуммирует. Остальные колонки (2, 3 и 4), не указанные ни в одном из параметров, будут безвозвратно удалены из структуры таблицы.
Разберем вторую ошибку, упомянутую в обсуждении, когда разработчик жалуется на "ерунду с суммой". Проблема часто кроется в том, как именно распределены имена колонок между первым и вторым параметром метода.
Посмотрим на два варианта вызова:
тз.Свернуть("f_инструмент_код, f_расход", "")тз.Свернуть("f_инструмент_код", "f_расход")В первом случае мы говорим системе: "Сгруппируй таблицу так, чтобы сочетание кода инструмента и суммы расхода было уникальным". Если в исходных данных были строки с одинаковым кодом, но разными суммами расхода, они не свернутся. Система посчитает их разными записями. В итоге сумма не накопится, а дубли останутся.
Во втором случае мы четко указываем: f_инструмент_код — это то, по чему мы группируем (измерение), а f_расход — это то, что мы суммируем (ресурс). Только такой синтаксис позволит "схлопнуть" записи по Сбербанку или любому другому инструменту и получить итоговый расход по нему.
Проанализируем более глубокие технические нюансы, которые не всегда очевидны. Метод Свернуть() крайне чувствителен к типам данных в колонках группировки. Для системы значения считаются идентичными только при полном совпадении типа и содержания.
Нюанс №1: NULL и Неопределено.
Если в одной строке таблицы в колонке группировки стоит NULL, а в другой Неопределено, метод воспримет их как разные значения. В результате в свернутой таблице вы увидите две строки, которые визуально (в отладчике или пустых ячейках) могут выглядеть одинаково. Перед сворачиванием рекомендуется привести такие значения к одному типу.
Нюанс №2: Регистр строк и лишние пробелы. Строковые значения сравниваются с учетом регистра и невидимых символов. Рассмотрим ситуацию:
Для метода Свернуть() это три абсолютно разные записи. Перед выполнением сворачивания крайне полезно сделать обход таблицы и обработать строковые колонки функцией СокрЛП(), а при необходимости привести их к единому регистру через ВРег() или НРег().
Для Каждого СтрокаТЗ Из тзМояТаблица Цикл
СтрокаТЗ.f_инструмент_код = СокрЛП(СтрокаТЗ.f_инструмент_код);
КонецЦикла;
тзМояТаблица.Свернуть("f_инструмент_код", "f_расход");
Посмотрим, что происходит с колонками, по которым идет расчет суммы. Важно понимать: 1. Тип данных: Колонки для суммирования должны иметь числовой тип. Если вы укажете там колонку со ссылками или строками, система не выдаст ошибку во время выполнения, но результат будет непредсказуемым — чаще всего значения просто проигнорируются или заменятся на 0.
2. Квалификаторы числа: Если в описании типов колонки не задана достаточная точность или длина, при суммировании больших чисел или дробных частей может произойти потеря точности или неожиданное округление. Проверяйте структуру таблицы перед заполнением.
Рассмотрим важный аспект производительности. Метод Свернуть() — это деструктивная операция для индексов. Если вы ранее создали индексы для быстрого поиска в таблице (через ТаблицаЗначений.Индексы.Добавить()), то после выполнения Свернуть() все они будут удалены.
Это означает, что если после сворачивания вы планируете использовать методы Найти() или НайтиСтроки() в цикле по этой же таблице, вам придется пересоздавать индексы заново. На больших объемах данных это может существенно замедлить работу алгоритма.
К слову о больших объемах: если ваша таблица содержит сотни тысяч строк, метод Свернуть() может проигрывать в скорости механизмам СУБД. В таких случаях мы рекомендуем рассмотреть альтернативный путь:
СГРУППИРОВАТЬ ПО.Запросы зачастую лучше справляются с управлением памятью и оптимизацией группировок на огромных массивах данных.
Иногда метод Свернуть() не подходит по условиям задачи — например, если вам нужно при группировке не суммировать числа, а выбирать максимальную дату или конкатенировать строки. В таких случаях мы используем объект Соответствие (или Структура, если ключи — строки).
Проанализируем пример "ручного" сворачивания через Соответствие:
СоответствиеГруппировок = Новый Соответствие;
Для Каждого Стр Из тзИсходная Цикл
Ключ = Стр.f_инструмент_код; // Здесь может быть структура для составного ключа
НайденнаяДанная = СоответствиеГруппировок.Получить(Ключ);
Если НайденнаяДанная = Неопределено Тогда
СоответствиеГруппировок.Вставить(Ключ, Стр.f_расход);
Иначе
СоответствиеГруппировок.Вставить(Ключ, НайденнаяДанная + Стр.f_расход);
КонецЕсли;
КонецЦикла;
// Далее заполняем новую таблицу из соответствия
Такой подход дает больше гибкости, хотя и требует написания большего количества кода. Но для стандартных задач агрегации метод Свернуть() остается самым быстрым и лаконичным решением, если соблюдать правила указания параметров и следить за чистотой данных в колонках.
Чтобы метод Свернуть() работал корректно, всегда проверяйте три вещи:
СокрЛП() и следите за отсутствием NULL в ключевых полях.