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