Почему метод Свернуть() в таблице значений 1С оставляет дубли или неверно считает суммы?

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

Метод Свернуть() является одним из самых часто используемых инструментов при работе с объектом ТаблицаЗначений. Казалось бы, его логика проста: сгруппировать одинаковые строки и просуммировать значения. Однако на практике разработчики, особенно начинающие, часто сталкиваются со "странным" поведением метода: дубликаты не исчезают, а суммы считаются некорректно. Разберем детально, как работает этот механизм, какие ошибки при его вызове наиболее типичны и на что нужно обратить внимание, чтобы результат всегда был предсказуемым.

Понимание принципа группировки: почему остаются дубли?

Проанализируем первую типичную ситуацию. Разработчик вызывает метод следующим образом:


тзМояТаблица.Свернуть("ИмяКолонки1, ИмяКолонки2, ИмяКолонки3, ИмяКолонки4", "ИмяКолонки5");

Ожидание: в ИмяКолонки1 должны остаться только уникальные значения. Реальность: в таблице по-прежнему видны строки, где ИмяКолонки1 повторяется.

Выясним причину такого поведения. Метод Свернуть() воспринимает первый параметр (колонки группировки) не как список по отдельности, а как составной ключ уникальности. Это можно сравнить с измерениями в регистре накопления или условием СГРУППИРОВАТЬ ПО в языке запросов SQL/1С.

Рассмотрим на примере. Если у нас есть три строки:

  • Сбербанк, Москва, Основной, 100
  • Сбербанк, Питер, Основной, 200
  • Сбербанк, Москва, Дополнительный, 300
  • Если мы выполним Свернуть("Организация, Город, Склад", "Сумма"), ни одна строка не схлопнется. Почему? Потому что сочетания значений во всех трех колонках группировки разные. Для системы "Сбербанк + Москва" и "Сбербанк + Питер" — это разные сущности. Чтобы получить уникальный список организаций, в первом параметре должна остаться только одна колонка:

    
    тзМояТаблица.Свернуть("ИмяКолонки1", "ИмяКолонки5");
    

    В этом случае система оставит только уникальные значения из первой колонки, а значения из пятой колонки просуммирует. Остальные колонки (2, 3 и 4), не указанные ни в одном из параметров, будут безвозвратно удалены из структуры таблицы.

    Сила кавычек и синтаксическая точность

    Разберем вторую ошибку, упомянутую в обсуждении, когда разработчик жалуется на "ерунду с суммой". Проблема часто кроется в том, как именно распределены имена колонок между первым и вторым параметром метода.

    Посмотрим на два варианта вызова:

    1. Ошибочный: тз.Свернуть("f_инструмент_код, f_расход", "")
    2. Правильный: тз.Свернуть("f_инструмент_код", "f_расход")

    В первом случае мы говорим системе: "Сгруппируй таблицу так, чтобы сочетание кода инструмента и суммы расхода было уникальным". Если в исходных данных были строки с одинаковым кодом, но разными суммами расхода, они не свернутся. Система посчитает их разными записями. В итоге сумма не накопится, а дубли останутся.

    Во втором случае мы четко указываем: f_инструмент_код — это то, по чему мы группируем (измерение), а f_расход — это то, что мы суммируем (ресурс). Только такой синтаксис позволит "схлопнуть" записи по Сбербанку или любому другому инструменту и получить итоговый расход по нему.

    Влияние типов данных на результат группировки

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

    Нюанс №1: NULL и Неопределено. Если в одной строке таблицы в колонке группировки стоит NULL, а в другой Неопределено, метод воспримет их как разные значения. В результате в свернутой таблице вы увидите две строки, которые визуально (в отладчике или пустых ячейках) могут выглядеть одинаково. Перед сворачиванием рекомендуется привести такие значения к одному типу.

    Нюанс №2: Регистр строк и лишние пробелы. Строковые значения сравниваются с учетом регистра и невидимых символов. Рассмотрим ситуацию:

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

    
    Для Каждого СтрокаТЗ Из тзМояТаблица Цикл
        СтрокаТЗ.f_инструмент_код = СокрЛП(СтрокаТЗ.f_инструмент_код);
    КонецЦикла;
    тзМояТаблица.Свернуть("f_инструмент_код", "f_расход");
    

    Особенности колонок суммирования

    Посмотрим, что происходит с колонками, по которым идет расчет суммы. Важно понимать: 1. Тип данных: Колонки для суммирования должны иметь числовой тип. Если вы укажете там колонку со ссылками или строками, система не выдаст ошибку во время выполнения, но результат будет непредсказуемым — чаще всего значения просто проигнорируются или заменятся на 0.

    2. Квалификаторы числа: Если в описании типов колонки не задана достаточная точность или длина, при суммировании больших чисел или дробных частей может произойти потеря точности или неожиданное округление. Проверяйте структуру таблицы перед заполнением.

    Удаление индексов и производительность

    Рассмотрим важный аспект производительности. Метод Свернуть() — это деструктивная операция для индексов. Если вы ранее создали индексы для быстрого поиска в таблице (через ТаблицаЗначений.Индексы.Добавить()), то после выполнения Свернуть() все они будут удалены.

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

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

    1. Выгрузить таблицу значений во временную таблицу запроса.
    2. Выполнить запрос с использованием СГРУППИРОВАТЬ ПО.
    3. Выгрузить результат обратно в таблицу значений.

    Запросы зачастую лучше справляются с управлением памятью и оптимизацией группировок на огромных массивах данных.

    Альтернативные методы решения задачи

    Иногда метод Свернуть() не подходит по условиям задачи — например, если вам нужно при группировке не суммировать числа, а выбирать максимальную дату или конкатенировать строки. В таких случаях мы используем объект Соответствие (или Структура, если ключи — строки).

    Проанализируем пример "ручного" сворачивания через Соответствие:

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

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

    Подведем итог

    Чтобы метод Свернуть() работал корректно, всегда проверяйте три вещи:

    ← На главную