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