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