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