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