Группировка таблицы значений в 1С: как свернуть строки и просуммировать итоги
Содержание
Свернуть таблицу значений по колонке в 1С можно двумя способами: вызвать метод Свернуть() у самой таблицы или сгруппировать данные запросом ещё до того, как они попали в таблицу. Метод проще и работает с тем, что уже лежит в памяти. Запрос быстрее на больших выборках и умеет больше агрегатных функций.
Метод принимает два списка колонок в виде строк: по каким полям склеивать строки и какие числовые поля суммировать. Строки с одинаковыми значениями в колонках группировки превращаются в одну, а числа в колонках итогов складываются. Всё остальное метод удаляет. Ниже — как это устроено, где метод подводит и чем его заменить.
Как работает метод Свернуть: что происходит со строками
Метод меняет таблицу на месте. Он не возвращает новую таблицу, а перестраивает ту, у которой вызван. Порядок действий внутри такой:
- Берутся колонки из первого списка (группировка).
- Строки с одинаковым набором значений в этих колонках объединяются в одну.
- В объединённой строке колонки из второго списка получают сумму значений всех слитых строк.
- Колонки, которых нет ни в одном списке, из таблицы исчезают.
Последний пункт чаще всего удивляет новичков. Если в таблице были колонки Номенклатура, Склад, Количество, Комментарий, а вы свернули по Номенклатура с суммой по Количество, колонки Склад и Комментарий пропадут вместе с данными.
Пример на пальцах. Исходная таблица:
| Номенклатура | Склад | Количество | Сумма |
|---|---|---|---|
| Болт М8 | Основной | 10 | 150 |
| Гайка М8 | Основной | 20 | 100 |
| Болт М8 | Запасной | 5 | 75 |
| Болт М8 | Основной | 2 | 30 |
После сворачивания по Номенклатура с суммированием Количество, Сумма остаётся две строки:
| Номенклатура | Количество | Сумма |
|---|---|---|
| Болт М8 | 17 | 255 |
| Гайка М8 | 20 | 100 |
Проверка: 10 + 5 + 2 = 17 штук болтов, 150 + 75 + 30 = 255 по сумме.
Тип таблицы не важен: метод одинаково работает с таблицей, созданной в коде, с результатом Выгрузить() из запроса и с таблицей значений реквизита формы.
Синтаксис и параметры: как вызвать Свернуть правильно
Общий вид вызова:
Таблица.Свернуть("КолонкиГруппировки", "КолонкиСуммирования");
Оба параметра — строки, имена колонок перечисляются через запятую. Пробелы после запятых допустимы. Второй параметр можно оставить пустой строкой, тогда метод просто уберёт дубли по колонкам группировки, ничего не складывая.
Полный пример с заполнением таблицы:
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура");
Таблица.Колонки.Добавить("Склад");
Таблица.Колонки.Добавить("Количество");
Таблица.Колонки.Добавить("Сумма");
Строка = Таблица.Добавить();
Строка.Номенклатура = "Болт М8";
Строка.Склад = "Основной";
Строка.Количество = 10;
Строка.Сумма = 150;
Строка = Таблица.Добавить();
Строка.Номенклатура = "Болт М8";
Строка.Склад = "Запасной";
Строка.Количество = 5;
Строка.Сумма = 75;
Таблица.Свернуть("Номенклатура", "Количество, Сумма");
После вызова в таблице одна строка: Болт М8, количество 15, сумма 225.
Три приёма, без которых метод ломает данные
Сохраните копию, если исходные строки ещё нужны. Метод необратим: слитые строки назад не вернуть. Сделайте копию заранее:
Итоги = Таблица.Скопировать();
Итоги.Свернуть("Номенклатура", "Количество, Сумма");
Теперь Таблица осталась как была, а Итоги содержит свёрнутый результат.
Копируйте только нужные колонки. Если свёртка нужна по двум полям, не тащите в копию двадцать:
Итоги = Таблица.Скопировать(, "Номенклатура, Количество");
Итоги.Свернуть("Номенклатура", "Количество");
Первый параметр Скопировать() — отбор строк, его пропустили; второй — список нужных колонок.
Выгрузите уникальные значения одной колонки. Для списка без дублей есть отдельный метод ВыгрузитьКолонку(), но он вернёт повторы. Если нужен список уникальных номенклатур, свёртка с пустым вторым параметром делает это за один вызов.
Поля группировки и поля итогов: как их выбирать
Колонки группировки определяют, что считать «одинаковой» строкой. Две строки сливаются, только если значения во всех колонках группировки совпадают. Добавили в список Склад — болты с основного и запасного складов остаются разными строками.
Правила выбора простые:
- ➕ В группировку идут то, по чему вы хотите видеть итоги: номенклатура, контрагент, договор, склад, месяц.
- 🔢 В суммирование идут числа: количество, сумма, НДС, вес.
- ❌ Не ставьте одну колонку в оба списка: её роль станет неоднозначной.
- 🧹 Всё лишнее перед вызовом лучше убрать самим, чтобы не гадать, что осталось.
Если в группировку входит колонка со ссылками (например, СправочникСсылка.Номенклатура), сравнение идёт по самой ссылке. Две разные карточки с одинаковым названием — это два разных значения, они не склеятся. Пустая ссылка тоже считается значением, и все строки с пустой ссылкой сольются в одну.
Для даты работает то же правило: сравнивается точное значение, вместе со временем. Если нужны итоги по дню или месяцу, сначала приведите дату к началу периода:
Для Каждого Строка Из Таблица Цикл
Строка.Период = НачалоМесяца(Строка.Период);
КонецЦикла;
Таблица.Свернуть("Период, Номенклатура", "Сумма");
Без этого шага документы одного месяца с разным временем проведения останутся отдельными строками.
Нечисловые данные при свёртке: строки, даты, булево
Суммирование определено для чисел. Колонки со строками, датами, ссылками и булевыми значениями в список итогов ставить не нужно: складывать их нечем. Что с ними делать, зависит от задачи.
| Что нужно | Как поступить |
|---|---|
| Строка — признак группы (название, артикул) | Поставить в колонки группировки |
| Строка — произвольный текст (комментарий), нужно собрать все в одну | Свёрткой не получится, соберите в цикле через разделитель |
| Дата — самая ранняя или поздняя | Запрос с МИНИМУМ() или МАКСИМУМ() либо цикл |
| Булево — «хоть одна истина» | Цикл или запрос с МАКСИМУМ() |
| Ссылка — любая из группы | Запрос с агрегатной функцией либо цикл |
Склеивание текстов выглядит так:
Тексты = Новый Соответствие;
Для Каждого Строка Из Таблица Цикл
Текущий = Тексты.Получить(Строка.Номенклатура);
Если Текущий = Неопределено Тогда
Тексты.Вставить(Строка.Номенклатура, Строка.Комментарий);
Иначе
Тексты.Вставить(Строка.Номенклатура, Текущий + "; " + Строка.Комментарий);
КонецЕсли;
КонецЦикла;
Сначала собираете соответствие «ключ → объединённый текст», потом, при необходимости, раскладываете в свёрнутую таблицу.
Если в колонке итогов попадаются Неопределено или пустая строка, не надейтесь, что метод сам всё приведёт. Заранее задайте типы колонок при создании: Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2))). Тогда пустое значение превратится в ноль.
Колонка с числовым типом, но пустым значением в части строк, ведёт себя безопасно только если тип задан. Для таблицы без типов (Колонки.Добавить("Сумма")) значение может оказаться чем угодно, включая строку.
Скорость на больших таблицах: что делать при сотнях тысяч строк
Свёртка в памяти — операция быстрая, но на миллионе строк с несколькими колонками группировки она заметна. Что помогает:
- Группируйте на стороне базы. Запрос с
СГРУППИРОВАТЬ ПОуменьшит выборку до того, как данные попадут на клиент или в таблицу значений. - Отрежьте лишние колонки до свёртки. Чем уже таблица, тем меньше памяти и времени. Для этого и нужен второй параметр
Скопировать(). - Уменьшите число колонок группировки. Каждая добавленная колонка — ещё одно сравнение для каждой пары строк.
- Выполняйте свёртку на сервере. Не гоняйте большую таблицу между клиентом и сервером: передача объёмной таблицы стоит дороже самой свёртки. Вызывайте метод в серверной процедуре и возвращайте на клиент уже готовый результат.
- Не сворачивайте в цикле по одной строке. Накопите данные и вызовите метод один раз.
Ориентир такой: до десятков тысяч строк свёртка в памяти не требует оптимизации, дальше имеет смысл мерить время через ТекущаяУниверсальнаяДатаВМиллисекундах() до и после вызова.
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Таблица.Свернуть("Номенклатура, Склад", "Количество, Сумма");
Затрачено = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;
Сообщить("Свёртка заняла, мс: " + Формат(Затрачено, "ЧГ=0"));
Если таблица пришла из запроса, самый короткий путь — вообще не сворачивать её потом, а вернуть уже сгруппированной.
Типичные ошибки при сворачивании и как их исправить
Чаще всего проблемы одни и те же. Сверьтесь с таблицей:
| Симптом | Причина | Что делать |
|---|---|---|
| Колонка пропала после свёртки | Её не было ни в группировке, ни в итогах | Добавить в нужный список или заранее скопировать данные |
| Итоги не сходятся с ожидаемыми | Лишняя колонка в группировке не даёт строкам слиться | Убрать колонку из первого списка |
| Строки с одним названием не слиплись | Это разные ссылки или разные регистры и пробелы в строках | Привести значения к единому виду до свёртки |
| Ошибка про отсутствующую колонку | Опечатка в имени или лишний пробел внутри имени | Сверить имена с Таблица.Колонки |
| Сумма получилась строкой или ошибкой | Колонка итогов не числовая | Указать числовой тип при создании колонки |
| Исходные данные пропали | Свернули саму таблицу, не копию | Использовать Скопировать() перед Свернуть() |
| Дробные «хвосты» вида 0.30000000000000004 | Накопление округления при сложении | Округлить результат Окр(Значение, 2) после свёртки |
Для отладки пригодится проверка набора колонок. Выведите их список прямо перед вызовом:
Для Каждого Колонка Из Таблица.Колонки Цикл
Сообщить(Колонка.Имя);
КонецЦикла;
Так сразу видно, не вылетела ли нужная колонка на предыдущем шаге.
Чем заменить метод: запрос, группировка вручную, макет отчёта
Метод Свернуть() — не единственный способ получить итоги. Иногда другой подход лучше.
Группировка запросом
Если данные берутся из базы, пусть группирует сама база:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| СУММА(Т.Количество) КАК Количество,
| МАКСИМУМ(Т.Дата) КАК ПоследняяДата
|ИЗ
| РегистрНакопления.ТоварыНаСкладах КАК Т
|СГРУППИРОВАТЬ ПО
| Т.Номенклатура";
Итоги = Запрос.Выполнить().Выгрузить();
Здесь доступны СУММА, МИНИМУМ, МАКСИМУМ, СРЕДНЕЕ, КОЛИЧЕСТВО. Метод таблицы так не умеет: он только складывает. Имя регистра в примере условное, подставьте свой объект.
Запрос к самой таблице значений
Временную таблицу можно передать в запрос и сгруппировать там же, если нужны не только суммы:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТДанные
|ИЗ
| &Таблица КАК Т
|;
|ВЫБРАТЬ
| Номенклатура,
| МАКСИМУМ(Количество) КАК МаксКоличество
|ИЗ
| ВТДанные
|СГРУППИРОВАТЬ ПО
| Номенклатура";
Запрос.УстановитьПараметр("Таблица", Таблица);
Результат = Запрос.Выполнить().Выгрузить();
Типы колонок в передаваемой таблице должны быть заданы, иначе запрос не поймёт, как с ними работать.
Цикл с соответствием
Подходит, когда нужна нестандартная логика слияния: брать не сумму, а первое значение, объединять тексты, считать долю. Накопили результат в Соответствие по ключу, потом разложили.
Что выбрать
| Ситуация | Лучший вариант |
|---|---|
| Данные в таблице, нужна только сумма | Свернуть() |
| Данные в базе | Запрос с СГРУППИРОВАТЬ ПО |
| Нужны минимум, максимум, среднее, количество | Запрос |
| Нужна особая логика слияния | Цикл и Соответствие |
| Данные в табличной части документа | Скопировать в таблицу значений и свернуть копию |
Если колонок группировки несколько и нужно быстро найти конкретную свёрнутую строку, пригодится Найти() (по одному значению в указанных колонках) и НайтиСтроки() (по структуре отбора). Они работают быстрее перебора вручную.
Частые вопросы
Можно ли свернуть таблицу значений по нескольким колонкам?
Таблица.Свернуть("Номенклатура, Склад", "Количество"). Строки сольются, только если совпали значения во всех указанных колонках.Что вернёт метод Свернуть()?
Скопировать().Можно ли отменить свёртку?
Как убрать дубли строк без суммирования?
Таблица.Свернуть("Колонка1, Колонка2", ""). Останутся уникальные сочетания значений.Как получить среднее или максимум по группе?
СГРУППИРОВАТЬ ПО и функциями СРЕДНЕЕ, МАКСИМУМ, МИНИМУМ, либо цикл с накоплением.