Почему пропадают пустые значения остатков в запросах 1С и как правильно соединять таблицы?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Анализ исходной проблемы и ошибок в запросе

Рассмотрим пример запроса, в котором разработчик пытается получить номенклатуру и ее остаток из регистра накопления:


ВЫБРАТЬ ПЕРВЫЕ 30 
    СпрНоменклатура.Ссылка КАК Ссылка, 
    СпрНоменклатура.Код КАК Код, 
    ВЫБОР КОГДА ТоварыОрганизацийОстатки.КоличествоОстаток = "" ТОГДА "0" ИНАЧЕ ТоварыОрганизацийОстатки.КоличествоОстаток КОНЕЦ КАК Поле1 
ИЗ Справочник.Номенклатура КАК СпрНоменклатура, 
    РегистрНакопления.ТоварыОрганизаций.Остатки КАК ТоварыОрганизацийОстатки 
ГДЕ НЕ СпрНоменклатура.ЭтоГруппа 
    И СпрНоменклатура.Ссылка = &Ссылка 
    И ТоварыОрганизацийОстатки.АналитикаУчетаНоменклатуры.Номенклатура.Ссылка = &Ссылка

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

Ошибка 1: Декартово произведение вместо явного соединения

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

В нашем случае, чтобы вывести все товары (даже те, которых нет на остатках), мы должны использовать левое соединение (LEFT JOIN):


ИЗ Справочник.Номенклатура КАК СпрНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций.Остатки КАК ТоварыОрганизацийОстатки
ПО СпрНоменклатура.Ссылка = ТоварыОрганизацийОстатки.АналитикаУчетаНоменклатуры.Номенклатура

Ошибка 2: Использование секции ГДЕ при левом соединении

Посмотрим на условие: ТоварыОрганизацийОстатки.АналитикаУчетаНоменклатуры.Номенклатура.Ссылка = &Ссылка, расположенное в секции ГДЕ. Это классическая причина того, почему исчезают пустые остатки.

Когда мы делаем левое соединение со справочником, для товаров, которых нет в регистре остатков, в поля регистра подставляется значение NULL (отсутствие значения). Накладывая в секции ГДЕ жесткое условие на присоединяемую таблицу, система неявно требует, чтобы это поле существовало. Но NULL не равен &Ссылка. В итоге условие дает ложь, и левое соединение фактически превращается во внутреннее соединение, полностью отсекая все товары с пустыми остатками.

Ошибка 3: Фильтрация остатков вместо использования параметров виртуальной таблицы

Самый оптимальный способ фильтровать данные из регистров в 1С — это использовать параметры виртуальных таблиц. Вместо того чтобы вытягивать все остатки, соединять их со справочником и только потом фильтровать по ссылке, передадим отбор непосредственно в саму таблицу остатков:


РегистрНакопления.ТоварыОрганизаций.Остатки(, АналитикаУчетаНоменклатуры.Номенклатура = &Ссылка)

Этот подход колоссально экономит ресурсы СУБД, так как система изначально извлекает остатки только по нужной нам аналитике, а не читает весь регистр целиком.

Ошибка 4: Неправильная работа с отсутствующими значениями (NULL и пустота)

В исходном запросе разработчик пытался проверить остаток на пустую строку: ТоварыОрганизацийОстатки.КоличествоОстаток = "". Выясним причину, почему это не работает. В 1С строгая типизация данных в запросах. Число нельзя сравнивать со строкой. Более того, при отсутствии связанной записи в регистре, поле получает значение не ноль, не пустую строку и даже не НЕОПРЕДЕЛЕНО. Оно получает значение NULL.

Разберем разницу:

  1. NULL — это результат левого, правого или полного соединения таблиц, обозначающий, что строка во второй таблице не найдена.
  2. НЕОПРЕДЕЛЕНО — это пустое значение для полей составного типа, физически хранящееся в базе данных.
  3. "" (пустая строка) — это просто строковое значение, не имеющее отношения к числам.

Также важно понимать разницу между выражениями = NULL и ЕСТЬ NULL. Сравнение = NULL вернет сам NULL, а не "Истина" или "Ложь", поэтому использовать знак равенства для проверки на NULL нельзя. Правильно писать ЕСТЬ NULL.

Вместо громоздкой конструкции ВЫБОР КОГДА ... ЕСТЬ NULL ТОГДА 0 ..., используем специализированную функцию ЕСТЬNULL. Она работает быстрее, так как транслируется платформой 1С на уровне SQL в оптимизированные функции СУБД (например, ISNULL или COALESCE).

ЕСТЬNULL(ТоварыОрганизацийОстатки.КоличествоОстаток, 0) КАК Остаток

Итоговое правильное решение

Соберем все наши знания воедино и напишем корректный запрос, который будет работать быстро и без ошибок выведет товар, даже если его нет на остатке.


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

В этом запросе мы применили правильное левое соединение, отфильтровали регистр остатков с помощью параметров виртуальной таблицы, исключили фильтрацию присоединенной таблицы в секции ГДЕ и грамотно обработали пустое значение NULL, заменив его на числовой ноль с помощью функции ЕСТЬNULL.

← На главную