Как устроена группировка данных в запросах 1С 8.3 и почему она тормозит
Содержание
Группировка в запросе 1С — это секция СГРУППИРОВАТЬ ПО: она сворачивает строки с одинаковыми значениями полей в одну и считает по ним агрегатные функции СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ. Каждое поле в ВЫБРАТЬ, которое не обёрнуто в агрегатную функцию, обязано стоять в группировке. Иначе получите ошибку Поле не входит в группу.
Частое заблуждение: «группировкой запросов» называют пакетный запрос, когда несколько запросов отправляют одним текстом через ;. Это другой механизм. Пакет экономит обращения к серверу, а группировка меняет сам результат: из тысяч строк движений получается одна строка на товар, склад или контрагента. Всё ниже относится к платформе 8.3 и одинаково для любых конфигураций: БП 3.0, ЗУП 3.1, УТ 11.
Как платформа выполняет группировку в запросе
Запрос на языке 1С не исполняется «сам по себе». Платформа переводит его в SQL для конкретной СУБД.
- 🗄️ Клиент-серверная база
(MS SQL Server, PostgreSQL).
СГРУППИРОВАТЬ ПОпревращается вGROUP BY. Считает рабочий процесс СУБД, а сервер 1С получает уже свёрнутые строки. - 📁 Файловая база. Группировку выполняет встроенный движок платформы прямо в процессе клиента или веб-сервера. Отсюда разница в скорости: один и тот же отчёт на файловой базе в 10 ГБ может считаться минуты.
- 🔗 Разыменование ссылок. Поле вида
Товары.Номенклатура.Родительв группировке добавляет в SQL соединение с таблицей справочника. Каждая точка в пути — ещё одно соединение.
Отсюда первый практический вывод: тяжесть группировки определяет не число строк результата, а число строк, которые СУБД прочитает до свёртки. Отчёт на 20 строк может перебрать 5 млн записей регистра.
Где группировка встречается кроме текста запроса
| Где | Как задаётся | Кто считает |
|---|---|---|
| Текст запроса | СГРУППИРОВАТЬ ПО, ИТОГИ ПО | СУБД, итоги — частично сервер 1С |
Виртуальные таблицы регистров (Остатки, Обороты) | Неявно, по измерениям | СУБД, по таблицам итогов |
| Схема компоновки данных (СКД) | Структура отчёта: группировки, детальные записи | Платформа строит запрос сама, затем компонует результат |
| Динамический список | Настройка «Группировка» в форме списка | Платформа, при каждом открытии |
Синтаксис группировки в языке запросов 1С
Базовый пример — продажи по номенклатуре за период с отбором крупных позиций:
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
СУММА(Продажи.Количество) КАК Количество,
СУММА(Продажи.Сумма) КАК Сумма,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Продажи.Ссылка) КАК ЧислоДокументов
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Продажи
ГДЕ
Продажи.Ссылка.Проведен
И Продажи.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
ИМЕЮЩИЕ
СУММА(Продажи.Сумма) > 100000
Три секции и их порядок
ГДЕотбирает строки до группировки. Сюда ставьте все условия на исходные поля.СГРУППИРОВАТЬ ПОсворачивает оставшиеся строки.ИМЕЮЩИЕфильтрует уже свёрнутые группы. Здесь работают условия на агрегаты: «сумма больше 100 000», «документов больше трёх».
Условие на обычное поле можно написать и в ИМЕЮЩИЕ, но это лишняя работа: СУБД сначала сгруппирует всё, а потом выбросит часть. Такие условия место в ГДЕ.
Агрегатные функции и NULL
- ➕
СУММАиСРЕДНЕЕ— только для чисел. - 🔢
КОЛИЧЕСТВО(*)считает строки,КОЛИЧЕСТВО(Поле)— непустые значения,КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле)— уникальные. - ⬆️
МАКСИМУМиМИНИМУМработают с числами, датами и строками. - ⚠️ Если в группе нет ни одного значения,
СУММАвернётNULL, а не ноль. При соединении с другой таблицей оборачивайте:ЕСТЬNULL(СУММА(...), 0).
Группировка без агрегатов и РАЗЛИЧНЫЕ
ВЫБРАТЬ РАЗЛИЧНЫЕ Поле и ВЫБРАТЬ Поле ... СГРУППИРОВАТЬ ПО Поле дают одинаковый результат. На СУБД они обычно выполняются одинаково. Выбирайте по смыслу: если дальше нужны агрегаты — группировка, если просто убрать дубли — РАЗЛИЧНЫЕ.
ИТОГИ: группировка поверх детальных строк
Секция ИТОГИ не сворачивает данные, а добавляет над ними строки-итоги. Детальные записи остаются:
ВЫБРАТЬ
Остатки.Склад КАК Склад,
Остатки.Номенклатура КАК Номенклатура,
Остатки.КоличествоОстаток КАК Количество
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Остатки
ИТОГИ
СУММА(Количество)
ПО
ОБЩИЕ,
Склад
Имя регистра условное — в вашей конфигурации оно может быть другим. Обходят такой результат через Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам): сначала строка общего итога, внутри — склады, внутри складов — детальные строки. Для справочников с иерархией есть ПО Номенклатура ИЕРАРХИЯ — итоги по каждой группе справочника.
В свежих версиях платформы появилась конструкция СГРУППИРОВАТЬ ПО ГРУППИРУЮЩИМ НАБОРАМ — несколько разрезов свёртки в одном запросе. Если конструктор запроса её не предлагает, ваша версия платформы её ещё не поддерживает; точный синтаксис сверяйте в синтакс-помощнике.
Ошибки группировки и как их читать
Большая часть проблем с группировкой — не производительность, а неверный результат или отказ запроса выполняться.
Параметры кластера и СУБД, которые влияют на тяжёлую группировку
Сама группировка в настройках кластера не регулируется: отдельного «параметра группировки» нет. Но тяжёлый запрос со свёрткой миллионов строк упирается в память — и на стороне 1С, и на стороне СУБД.
Сервер 1С
В консоли администрирования серверов, в свойствах рабочего сервера, задаются пороги памяти рабочих процессов, в том числе Временно допустимый объём памяти процессов и Критический объём памяти процессов. Если отчёт тянет в рабочий процесс огромный результат (например, группировка с ИТОГИ по детальным записям за год), процесс может превысить порог. Тогда платформа прервёт вызов, а пользователь увидит ошибку нехватки памяти.
Порядок действий:
- Найдите отчёт, который вызвал рост памяти: события
CALLиEXCPв технологическом журнале. - Уменьшите объём, который уходит на сервер 1С: свёртку делайте в запросе, а не в коде или в макете отчёта.
- Только после этого поднимайте пороги — и с учётом свободной памяти сервера.
Изменение параметров рабочего сервера и перезапуск службы агента сервера 1С обрывают сеансы пользователей. Делайте это вне рабочего времени и с предупреждением.
СУБД
- 🐘 PostgreSQL. Хеш-группировка держит таблицу групп в памяти размером до
work_mem. Не хватает — данные уходят во временные файлы на диск, запрос замедляется в разы. Параметр меняют аккуратно: он действует на каждую операцию в каждом соединении. - 🪟 MS SQL Server. Большие сортировки и группировки при нехватке памяти сбрасываются в
tempdb. Медленный диск подtempdb— частая причина тормозов именно у отчётов со свёрткой. - 📊 Статистика. Устаревшая статистика ведёт к неверной оценке числа строк и плохому плану. Регламентное обновление статистики на СУБД — обязательная часть обслуживания любой базы 1С.
Блокировки и взаимоблокировки при чтении сгруппированных данных
Запрос с группировкой сам по себе только читает. Проблемы начинаются, когда он работает внутри транзакции: при проведении документа, при записи набора записей регистра.
Классический сценарий взаимоблокировки
- Два пользователя одновременно проводят расходные документы по одному складу.
- Каждый сеанс в транзакции читает остатки запросом к
Остаткис группировкой по номенклатуре. - Оба получили остатки и хотят записать движения в те же записи регистра.
- Каждый ждёт, пока другой отпустит данные. Один из сеансов получает ошибку со словами
Конфликт блокировок при выполнении транзакцииили превышение времени ожидания блокировки.
Как это лечат в управляемом режиме блокировок
Перед чтением остатков накладывают исключительную управляемую блокировку только на нужные записи. Тогда второй сеанс подождёт на входе, а не сцепится с первым посреди транзакции:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = ТаблицаТоваров;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
Блокировка.Заблокировать();
// дальше — запрос остатков с группировкой и запись движений
Здесь ТаблицаТоваров — таблица значений с колонкой Номенклатура, свёрнутая по товарам заранее. Блокировка ставится внутри транзакции: в обработке проведения она уже открыта платформой.
Что ещё снижает конфликты
- 🎯 Блокируйте узко: по складу и товарам документа, а не весь регистр.
- ⏱️ Держите транзакцию короткой. Тяжёлый аналитический запрос с группировкой за год не место внутри проведения.
- 📑 Отчёты выполняйте вне транзакций. Чтение вне транзакции в управляемом режиме не ставит управляемых блокировок 1С.
- 🧾 Ищите виновника в технологическом журнале: события
TLOCK,TTIMEOUT,TDEADLOCKпоказывают, кто кого ждал и на каком пространстве блокировок.
Как увидеть и улучшить план запроса с группировкой
План выполнения показывает, как СУБД читает данные: по индексу или полным просмотром, хешем или сортировкой группирует.
Где взять план
- 📝 Технологический журнал 1С. В
logcfg.xmlвключают свойство<plansql/>и событияDBMSSQL(MS SQL) илиDBPOSTGRS(PostgreSQL). В журнал попадут текст SQL и план. - 🔍 Средства СУБД. Для MS SQL — фактический план в SQL Server Management Studio, для PostgreSQL —
EXPLAIN (ANALYZE, BUFFERS)на тексте SQL из журнала. - 📁 Файловая база планов не показывает. Если отчёт тормозит только в ней, проверьте тот же запрос на копии в клиент-серверном варианте.
Что искать в плане
| Признак в плане | Что значит | Что менять в запросе 1С |
|---|---|---|
| Полный просмотр большой таблицы | Отбор не попадает в индекс | Условия на измерения — в параметры виртуальной таблицы, а не в ГДЕ |
| Сортировка или хеш с выгрузкой на диск | Группируется слишком много строк | Сузить период, сгруппировать раньше во временной таблице |
| Много соединений со справочниками | Разыменование полей через точку | Группировать по ссылке, реквизиты получать после свёртки |
| Вложенные циклы с огромным числом итераций | Неверная оценка строк | Обновить статистику, разбить запрос на пакет с временными таблицами |
Главное правило для регистров
Не группируйте таблицу движений, если нужен остаток или оборот. Виртуальные таблицы Остатки и Обороты берут данные из таблиц итогов, где свёртка уже сделана. Условие в параметрах виртуальной таблицы отбирает записи до расчёта:
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(
&Дата,
Склад = &Склад) КАК Остатки
То же условие в ГДЕ применится уже к рассчитанным остаткам по всем складам — дольше в разы на больших базах.
Переписываем код: от циклов к одной группировке
Чаще всего ускорение даёт не настройка сервера, а переделка кода. Три приёма, которые знает каждый, кто оптимизировал отчёты.
1. Запрос в цикле → один запрос с группировкой
Плохо: перебрать 3 000 товаров и для каждого выполнить запрос суммы продаж. Это 3 000 обращений к СУБД. Хорошо: один запрос со списком товаров во временной таблице, соединение и СГРУППИРОВАТЬ ПО. Обращение одно, результат тот же.
2. Сначала свернуть, потом соединять
Если нужно соединить продажи с оплатами, группируйте каждую сторону отдельно и соединяйте уже свёрнутые таблицы:
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
СУММА(Продажи.СуммаДокумента) КАК Сумма
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
Документ.РеализацияТоваровУслуг КАК Продажи
ГДЕ
Продажи.Проведен
И Продажи.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
Продажи.Контрагент
ИНДЕКСИРОВАТЬ ПО
Контрагент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Продажи.Контрагент КАК Контрагент,
ВТ_Продажи.Сумма КАК Продано,
ЕСТЬNULL(ВТ_Оплаты.Сумма, 0) КАК Оплачено
ИЗ
ВТ_Продажи КАК ВТ_Продажи
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Оплаты КАК ВТ_Оплаты
ПО ВТ_Продажи.Контрагент = ВТ_Оплаты.Контрагент
ВТ_Оплаты строится так же, отдельным запросом пакета. Так строки не размножаются, а соединение идёт по уже маленьким таблицам. ИНДЕКСИРОВАТЬ ПО имеет смысл, когда во временной таблице тысячи строк; для пары десятков он не нужен.
3. Группировка по ссылке, а не по реквизиту через точку
СГРУППИРОВАТЬ ПО Товары.Номенклатура.Родитель заставляет СУБД соединять табличную часть со справочником до свёртки — на каждой строке. Сгруппируйте по Товары.Номенклатура во временную таблицу, а родителя получите вторым запросом уже по свёрнутым строкам. При 1 млн строк продаж и 5 000 товаров соединение со справочником пройдёт по 5 000 строкам вместо миллиона.
Когда группировка в запросе не нужна
- 📊 Отчёт на СКД: группировки задаются в структуре отчёта. Ручная
СГРУППИРОВАТЬ ПОв наборе данных отнимает у пользователя возможность менять разрезы. - 🔁 Нужны и детальные строки, и итоги:
ИТОГИили СКД, а не два отдельных запроса. - 🧮 Данные уже в таблице значений на сервере: метод
Свернуть("Номенклатура", "Количество, Сумма")дешевле нового запроса.