Как правильно составить запрос к остаткам регистра накопления и избавиться от пустых строк

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

При разработке отчетов в системе 1С:Предприятие одной из самых частых задач является получение остатков товаров или резервов по конкретным документам. Часто начинающие разработчики сталкиваются с ситуацией, когда в результат запроса попадают лишние строки с пустыми значениями остатков (NULL). В этой статье мы подробно разберем, как правильно соединять таблицы документов и регистров, как эффективно фильтровать данные и на что обратить внимание, чтобы запрос работал молниеносно даже на больших объемах данных.

Разбор базовой проблемы: почему появляются пустые строки

Проанализируем исходную ситуацию. У нас есть временная таблица ВЗаказ, созданная на основе табличной части документа ВнутреннийЗаказ. Мы хотим соединить ее с остатками регистра ТоварыВРезервеНаСкладах. Автор исходного запроса использовал ЛЕВОЕ СОЕДИНЕНИЕ, что и привело к появлению строк, где текущий остаток равен NULL.

Рассмотрим, как работает ЛЕВОЕ СОЕДИНЕНИЕ: оно берет все записи из первой (левой) таблицы и пытается найти соответствия во второй (правой). Если во второй таблице данных нет (например, товар уже снят с резерва), система все равно оставит строку из заказа, но в полях регистра проставит NULL.

Если наша задача — вывести только те позиции, по которым есть остаток в резерве, нам необходимо изменить логику соединения или добавить фильтрацию.

Решение 1: Использование внутреннего соединения

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

Посмотрим на примере, как должен выглядеть исправленный второй запрос:


ВЫБРАТЬ
    ВЗаказ.Клиент,
    ВЗаказ.Канал,
    ВЗаказ.Ссылка КАК Заказ,
    ВЗаказ.Номенклатура,
    ВЗаказ.ДатаНачалаСрочногоРезерва,
    ВЗаказ.ДатаКонцаСрочногоРезерва,
    ВЗаказ.ПервоначальноеЗначениеРезерва КАК ПервоначальноеЗнач,
    ТоварыВРезервеНаСкладахОстатки.КоличествоОстаток КАК ТекЗначение
ИЗ
    ВЗаказ КАК ВЗаказ
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(&НаДату, ) КАК ТоварыВРезервеНаСкладахОстатки
        ПО ВЗаказ.Ссылка = ТоварыВРезервеНаСкладахОстатки.ДокументРезерва
            И ВЗаказ.Номенклатура = ТоварыВРезервеНаСкладахОстатки.Номенклатура

Решение 2: Обработка NULL и фильтрация в секции ГДЕ

Если по каким-то причинам нам все же требуется левое соединение (например, мы хотим видеть все строки заказа, но визуально скрыть те, где остаток обнулился), мы можем использовать функцию ЕСТЬNULL. Она позволяет заменить неопределенное значение на число (например, 0).

Проанализируем конструкцию условия:


ГДЕ
    ЕСТЬNULL(ТоварыВРезервеНаСкладахОстатки.КоличествоОстаток, 0) > 0

Важный нюанс: использование условий в секции ГДЕ на поля правой таблицы при ЛЕВОМ СОЕДИНЕНИИ фактически превращает его во внутреннее. Это происходит потому, что NULL не может быть больше 0, и такие строки отсекаются. Поэтому первый вариант (с явным внутренним соединением) считается более чистым и понятным для оптимизатора СУБД.

Оптимизация производительности: параметры виртуальных таблиц

Одной из критических ошибок в исходном запросе является обращение к виртуальной таблице Остатки без параметров. В этом случае система сначала получит все остатки по регистру из базы данных, а уже потом начнет их соединять с вашими заказами. Если в базе миллионы записей, отчет будет формироваться очень долго.

Разберем, как ускорить выполнение. Мы должны передать условие отбора прямо в параметры виртуальной таблицы. В 1С это делается через второй параметр метода Остатки.

Правильный подход выглядит так:

  1. Сначала выбираем нужные заказы во временную таблицу.
  2. Индексируем временную таблицу.
  3. При обращении к регистру передаем фильтр по списку номенклатуры или документов.

// Первый запрос: выбираем данные и помещаем в индексированную таблицу
ВЫБРАТЬ
    ВнутреннийЗаказТовары.Ссылка КАК Ссылка,
    ВнутреннийЗаказТовары.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВЗаказ
ИЗ
    Документ.ВнутреннийЗаказ.Товары КАК ВнутреннийЗаказТовары
ГДЕ
    ВнутреннийЗаказТовары.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон
ИНДЕКСИРОВАТЬ ПО
    Ссылка,
    Номенклатура ;

////////////////////////////////////////////////////////////////////////////////
// Второй запрос: используем параметры виртуальной таблицы
ВЫБРАТЬ
    ВЗаказ.Ссылка,
    ТоварыОстатки.КоличествоОстаток
ИЗ
    ВЗаказ КАК ВЗаказ
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(&НаДату, 
            ДокументРезерва В (ВЫБРАТЬ РАЗЛИЧНЫЕ Ссылка ИЗ ВЗаказ)
        ) КАК ТоварыОстатки
        ПО ВЗаказ.Ссылка = ТоварыОстатки.ДокументРезерва
            И ВЗаказ.Номенклатура = ТоварыОстатки.Номенклатура

Проблема разыменования полей через точку

Рассмотрим ситуацию, описанную в сообщениях форума, когда автор попытался упростить запрос, обратившись к дате заказа через ВнутреннийЗаказТовары.Ссылка.Дата. Это привело к значительному замедлению (отчет формировался более 10 минут).

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

Рекомендация: всегда используйте явное соединение с таблицей документа или сначала выбирайте нужные реквизиты документа во временную таблицу, а затем работайте с ними. Это дает вам полный контроль над планом выполнения запроса.

Работа с остатками в Системе компоновки данных (СКД)

Если вы используете СКД, важно помнить о нескольких особенностях:

Итоговые советы по написанию верного запроса

Подведем итоги нашего анализа. Чтобы ваш запрос к регистру накопления был корректным и быстрым, следуйте этим правилам:

  1. Выбирайте правильный тип соединения: используйте ВНУТРЕННЕЕ СОЕДИНЕНИЕ, если вам нужны только записи с существующими остатками.
  2. Используйте параметры виртуальных таблиц: никогда не делайте "голый" запрос к .Остатки(), если можно ограничить выборку по конкретным товарам или документам.
  3. Индексируйте временные таблицы: если вы используете ПОМЕСТИТЬ, всегда добавляйте ИНДЕКСИРОВАТЬ ПО для полей связи.
  4. Избегайте лишних точек: вместо Ссылка.Реквизит используйте соединение с физической таблицей объекта.
  5. Добавляйте ВЫБРАТЬ РАЗРЕШЕННЫЕ: это критически важно для корректной работы у пользователей с ограниченными правами (RLS).

Соблюдение этих простых правил позволит вам создавать надежные и производительные отчеты, которые будут стабильно работать даже при значительном росте объема данных в информационной базе.

← На главную