При работе с регистрами накопления в 1С программисты часто сталкиваются с необходимостью детализации данных. Одной из типичных задач является получение остатков не только по номенклатуре, но и в разрезе конкретных партий поступления. В этой статье мы разберем, как правильно составить запрос к виртуальной таблице остатков, почему в большинстве случаев не требуется ручная группировка и как корректно обработать пустые значения партий.
Рассмотрим ситуацию: у нас есть регистр накопления ЗапасыНаСкладах, который имеет измерения Организация, Номенклатура, Характеристика, Партия, СтруктурнаяЕдиница и Ячейка. Автор исходного запроса столкнулся с тем, что данные выводятся общей суммой по номенклатуре, игнорируя разбиение по партиям.
Первоначальный код запроса выглядел следующим образом:
ВЫБРАТЬ
ОстаткиНаСкладе.Номенклатура КАК Номенклатура,
ОстаткиНаСкладе.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ЗапасыНаСкладах.Остатки(&Дата,
Организация = &Организация
И Номенклатура = &Номенклатура
И СтруктурнаяЕдиница = &СтруктурнаяЕдиница) КАК ОстаткиНаСкладе
Проанализируем этот код. В секции ВЫБРАТЬ указана только Номенклатура. Несмотря на то что в самом регистре данные хранятся с детализацией до партии, виртуальная таблица Остатки работает по принципу автоматической свертки итогов по тем полям, которые выбрал разработчик.
Для того чтобы система "развернула" остатки по партиям, нам необходимо просто добавить поле Партия в список выбираемых полей. Рассмотрим исправленный вариант запроса:
ВЫБРАТЬ
ОстаткиНаСкладе.Номенклатура КАК Номенклатура,
ОстаткиНаСкладе.Партия КАК Партия,
ОстаткиНаСкладе.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ЗапасыНаСкладах.Остатки(&Дата,
Организация = &Организация
И Номенклатура = &Номенклатура
И СтруктурнаяЕдиница = &СтруктурнаяЕдиница) КАК ОстаткиНаСкладе
Как только мы добавляем ОстаткиНаСкладе.Партия, платформа 1С понимает, что итоги нужно рассчитать в разрезе партий. Если для одной номенклатуры на складе числится три разные партии, мы получим три строки в результате запроса.
Выясним причину, по которой нам не нужно использовать конструкцию СГРУППИРОВАТЬ ПО. Виртуальные таблицы 1С (Остатки, Обороты, ОстаткиИОбороты) — это не физические таблицы в базе данных SQL, а сложные подзапросы, которые генерирует платформа.
Проанализируем ситуацию подробнее: когда мы обращаемся к РегистрНакопления.ИмяРегистра.Остатки, 1С обращается к таблицам итогов. Платформа анализирует список полей в секции ВЫБРАТЬ и автоматически добавляет их в SQL-запрос в секцию GROUP BY. Таким образом, явное использование группировки в языке запросов 1С при работе с виртуальными таблицами является избыточным и может даже незначительно замедлить выполнение запроса, так как системе придется выполнять двойную работу.
Важное исключение: группировка СГРУППИРОВАТЬ ПО понадобится только в том случае, если вы используете агрегатные функции (например, СУММА или МАКСИМУМ) над полями, которые сами не являются ресурсами виртуальной таблицы, или если вы соединяете таблицу остатков с другими таблицами.
Часто возникает вопрос: как отобразить в отчете остатки, у которых партия не указана (например, товар был оприходован до начала ведения партионного учета)? В системе 1С пустая партия — это не NULL, а ПустаяСсылка соответствующего типа документа или справочника.
Рассмотрим, как сделать вывод данных более дружелюбным для пользователя с помощью функции ЕСТЬNULL или проверки на пустую ссылку:
ВЫБРАТЬ
ОстаткиНаСкладе.Номенклатура КАК Номенклатура,
ВЫБОР
КОГДА ОстаткиНаСкладе.Партия = ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка)
ТОГДА "--- Без партии ---"
ИНАЧЕ ОстаткиНаСкладе.Партия
КОНЕЦ КАК ПартияПредставление,
ОстаткиНаСкладе.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ЗапасыНаСкладах.Остатки(&Дата, ) КАК ОстаткиНаСкладе
Такой подход позволит четко отделить позиции без партий от остальных. Также стоит помнить, что использование ЕСТЬNULL(Партия, Неопределено) может быть полезно при левых соединениях, но в параметрах виртуальной таблицы остатков измерения обычно всегда заполнены хотя бы пустой ссылкой.
Разберем критически важный аспект производительности. Никогда не используйте секцию ГДЕ для фильтрации данных по измерениям в виртуальных таблицах, если это можно сделать через параметры.
Сравним два подхода:
ИЗ РегистрНакопления.Товары.Остатки() ГДЕ Номенклатура = &Номенклатура. В этом случае система сначала получит остатки ПО ВСЕМ товарам из базы, и только потом отфильтрует нужный.ИЗ РегистрНакопления.Товары.Остатки(, Номенклатура = &Номенклатура). В этом случае фильтр накладывается на уровне SQL-запроса к таблицам итогов, что работает в десятки и сотни раз быстрее.Проанализируем также порядок измерений. Индексы в таблицах итогов строятся по порядку измерений, заданному в метаданных. Старайтесь указывать фильтры в параметрах виртуальной таблицы в том же порядке, в котором измерения идут в самом регистре (обычно это Организация -> Номенклатура -> Склад).
Иногда при переходе к учету по партиям программист видит странную картину: общий остаток по номенклатуре равен нулю, но при добавлении в запрос поля Партия вылезают строки с положительными и отрицательными значениями (например, Партия №1: +10 шт, Партия №2: -10 шт).
Выясним причину: это происходит из-за ошибок в логике проведения документов, когда приход был по одной партии, а списание — по другой (или вовсе без указания партии). Использование детализации по партиям в запросе — отличный способ выявить такие "хвосты" и провести корректировку регистров. Если бы мы не добавили Партия в ВЫБРАТЬ, виртуальная таблица просто сложила бы +10 и -10, показав нам итоговый ноль, что скрыло бы проблему в учете.