Как правильно получить остатки товара по штрихкоду и почему пропадают записи при левом соединении

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

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

Разбор типичной ошибки: левое соединение и секция ГДЕ

Проанализируем ситуацию, с которой столкнулся автор темы. Имеется запрос, который должен соединить данные о штрихкодах и остатках. В исходном варианте автор использовал следующую конструкцию:


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

Выясним причину, по которой такой запрос "теряет" данные. Основная проблема здесь кроется в секции ГДЕ. Когда мы выполняем ЛЕВОЕ СОЕДИНЕНИЕ, система берет все записи из левой таблицы (в нашем случае это выборка по штрихкоду) и пытается найти соответствия в правой (остатки). Если остатка нет, поля правой таблицы заполняются значением NULL.

Однако, добавив условие ГДЕ ТоварыНаСкладахОстатки.Склад = &Склад, мы совершаем критическую ошибку. Для тех строк, где остатка нет, поле Склад будет содержать NULL. Сравнение NULL = &Склад всегда возвращает результат "Ложь". В итоге все строки с нулевыми остатками отсекаются, и наше ЛЕВОЕ СОЕДИНЕНИЕ фактически превращается во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Именно поэтому вместо трех позиций в результате остается только две.

Решение №1: Использование параметров виртуальных таблиц (Оптимальное)

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


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

Разберем, что мы изменили и почему это работает:

  1. Параметры виртуальной таблицы: Мы передали условие Склад = &Склад внутрь скобок Остатки(). Теперь система сначала отберет остатки только по нужному складу на уровне базы данных, а уже затем будет соединять этот готовый результат с таблицей штрихкодов.
  2. Функция ЕСТЬNULL: Чтобы в результате вместо пустоты (NULL) для отсутствующих товаров красовался честный ноль, мы обернули поле остатка в функцию ЕСТЬNULL(..., 0).
  3. Производительность: Использование параметров виртуальных таблиц позволяет SQL-серверу работать с гораздо меньшим объемом данных, так как фильтрация происходит до этапа соединения таблиц.

Решение №2: Использование временных таблиц

Если запрос в дальнейшем будет усложняться, хорошим тоном считается использование временных таблиц. Это делает код более читаемым и облегчает отладку в консоли запросов. Рассмотрим этот подход:


// Сначала найдем все товары по штрихкоду и поместим их во временную таблицу
ВЫБРАТЬ
    Штрихкоды.Владелец КАК Номенклатура,
    Штрихкоды.Характеристика КАК Характеристика
ПОМЕСТИТЬ ВТ_ТоварыПоШК
ИЗ
    РегистрСведений.Штрихкоды КАК Штрихкоды
ГДЕ
    Штрихкоды.Штрихкод = &Штрихкод
;

////////////////////////////////////////////////////////////////////////////////
// Теперь соединяем временную таблицу с остатками
ВЫБРАТЬ
    ВТ_ТоварыПоШК.Номенклатура КАК Номенклатура,
    ВТ_ТоварыПоШК.Характеристика КАК Характеристика,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток
ИЗ
    ВТ_ТоварыПоШК КАК ВТ_ТоварыПоШК
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад) КАК ТоварыНаСкладахОстатки
        ПО ВТ_ТоварыПоШК.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура
            И ВТ_ТоварыПоШК.Характеристика = ТоварыНаСкладахОстатки.Характеристика

Проанализируем преимущества этого метода. Во-первых, мы четко разделяем логику: сначала определяем "что ищем", а затем "какие свойства (остатки) у этого есть". Во-вторых, если бы данных по штрихкодам было очень много, мы могли бы проиндексировать временную таблицу ВТ_ТоварыПоШК по полям связи, что еще больше ускорило бы работу системы.

Важные технические нюансы при работе в "1С:Розница"

Работая с конфигурациями на базе БСП (Библиотеки стандартных подсистем), такими как "1С:Розница", стоит учитывать дополнительные факторы:

  1. Упаковки товара: В современных версиях "Розницы" штрихкод может быть привязан не к самому товару, а к его упаковке. Если вы просто соединяете штрихкод с номенклатурой, вы можете получить остаток в базовых единицах измерения, что может запутать пользователя. Проверяйте наличие измерения Упаковка в регистре штрихкодов.
  2. Характеристики: Если в программе включен учет по характеристикам, но для какого-то товара они не используются, поле Характеристика в регистрах будет содержать пустую ссылку. Важно, чтобы условие соединения ПО корректно обрабатывало эти связи (в 1С пустая ссылка одного типа всегда равна пустой ссылке того же типа).
  3. Маркировка: Для товаров, подлежащих обязательной маркировке, штрихкод (EAN-13) часто является лишь частью кода датаматрикс. Если логика вашего приложения предполагает работу с марками, обратите внимание на регистры сведений, хранящие штрихкоды упаковок.

Итоги и рекомендации

Мы выяснили, что "магия" исчезновения строк при левом соединении — это не ошибка платформы 1С, а классическое поведение SQL при фильтрации по необязательной таблице. Чтобы ваш запрос всегда возвращал правильный результат, придерживайтесь следующих правил:

Во-первых, никогда не ставьте условия на правую (присоединяемую) таблицу в секции ГДЕ. Это превращает левое соединение во внутреннее.

Во-вторых, используйте параметры виртуальных таблиц &Период, &Условие для предварительной фильтрации данных регистров накопления.

В-третьих, всегда используйте ЕСТЬNULL для числовых полей, которые приходят из правой таблицы, чтобы избежать ошибок при дальнейших арифметических операциях в коде или отчетах.

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

← На главную