Как правильно вычислить свободный остаток на складе в запросе 1С?

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

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

Анализ типичных ошибок в запросах на остатки

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

  1. Декартово произведение: Если просто перечислить таблицы в секции ИЗ без условия связи, СУБД попытается сопоставить каждую строку первой таблицы с каждой строкой второй. Это приводит к дублированию строк и неверным результатам.
  2. Отсутствие параметров виртуальных таблиц: Использование условий в секции ГДЕ для виртуальных таблиц Остатки — это критическая ошибка производительности. В этом случае система сначала выбирает все остатки из базы в память и только потом фильтрует их. Правильно передавать склад и номенклатуру в параметры: РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Номенклатура = &Номенклатура).
  3. Проблема NULL: Если товара на складе 10 штук, а резерва по нему нет вообще, то при соединении таблиц в поле резерва придет значение NULL. Математическая операция 10 - NULL даст в результате NULL, а не 10. Поэтому использование функции ЕСТЬNULL() обязательно.

Шаг 1: Подготовка временных таблиц

Разберем правильный подход, основанный на использовании временных таблиц. Это позволит нам структурировать запрос и сделать его читаемым. Сначала подготовим данные по физическим остаткам на складах.

Обратите внимание, что мы сразу накладываем фильтры внутри параметров виртуальной таблицы. Рассмотрим пример кода:


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

Проанализируем этот блок: мы выбрали только нужные нам разрезы (измерения) и поместили их во временную таблицу ВТ_Остатки. Аналогичным образом поступим с резервами и товарами, которые уже подготовлены к передаче (актуально для УТ 10.3):


ВЫБРАТЬ
    ТоварыВРезерве.Склад,
    ТоварыВРезерве.Номенклатура,
    ТоварыВРезерве.ХарактеристикаНоменклатуры,
    ТоварыВРезерве.КоличествоОстаток КАК КоличествоРезерв
ПОМЕСТИТЬ ВТ_Резервы
ИЗ
    РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(&КонДата, Склад = &Склад) КАК ТоварыВРезерве;

////////////////////////////////////////////////////////////////////////////////

ВЫБРАТЬ
    ТоварыКПередаче.Склад,
    ТоварыКПередаче.Номенклатура,
    ТоварыКПередаче.ХарактеристикаНоменклатуры,
    ТоварыКПередаче.КоличествоОстаток КАК КоличествоКПередаче
ПОМЕСТИТЬ ВТ_КПередаче
ИЗ
    РегистрНакопления.ТоварыКПередачеСоСкладов.Остатки(&КонДата, Склад = &Склад) КАК ТоварыКПередаче;

Шаг 2: Объединение данных и расчет свободного остатка

Теперь нам нужно собрать все данные воедино. Существует два основных способа: ЛЕВОЕ СОЕДИНЕНИЕ и ОБЪЕДИНИТЬ ВСЕ. Рассмотрим их особенности.

Вариант А: Левое соединение

Этот способ удобен, если мы уверены, что товар обязательно есть на остатке в основной таблице ВТ_Остатки. Мы присоединяем к ней резервы и товары к передаче по полям Номенклатура и ХарактеристикаНоменклатуры.


ВЫБРАТЬ
    ВТ_Остатки.Номенклатура,
    ВТ_Остатки.Характеристика,
    ВТ_Остатки.КоличествоОстаток КАК СкладскойОстаток,
    ЕСТЬNULL(ВТ_Резервы.КоличествоРезерв, 0) КАК Резерв,
    ЕСТЬNULL(ВТ_КПередаче.КоличествоКПередаче, 0) КАК КПередаче,
    ВТ_Остатки.КоличествоОстаток - ЕСТЬNULL(ВТ_Резервы.КоличествоРезерв, 0) - ЕСТЬNULL(ВТ_КПередаче.КоличествоКПередаче, 0) КАК СвободныйОстаток
ИЗ
    ВТ_Остатки КАК ВТ_Остатки
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Резервы КАК ВТ_Резервы
        ПО ВТ_Остатки.Номенклатура = ВТ_Резервы.Номенклатура
            И ВТ_Остатки.Характеристика = ВТ_Резервы.ХарактеристикаНоменклатуры
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_КПередаче КАК ВТ_КПередаче
        ПО ВТ_Остатки.Номенклатура = ВТ_КПередаче.Номенклатура
            И ВТ_Остатки.Характеристика = ВТ_КПередаче.ХарактеристикаНоменклатуры

Важный нюанс: Как справедливо замечено в сообщении 18 обсуждения, если товара нет на складе (остаток 0), но по нему завис ошибочный резерв, при ЛЕВОМ СОЕДИНЕНИИ мы эту строку не увидим. Чтобы увидеть все расхождения, лучше использовать метод объединения.

Вариант Б: Использование ОБЪЕДИНИТЬ ВСЕ (рекомендуется)

Этот метод считается более надежным и производительным для вычисления итоговых показателей из нескольких регистров. Мы собираем все данные в одну «кучу», а затем сворачиваем их группировкой.


ВЫБРАТЬ
    Т.Номенклатура,
    Т.Характеристика,
    СУММА(Т.Остаток) КАК Остаток,
    СУММА(Т.Резерв) КАК Резерв,
    СУММА(Т.Остаток - Т.Резерв) КАК Свободный
ИЗ
    (ВЫБРАТЬ
        ВТ_Остатки.Номенклатура,
        ВТ_Остатки.Характеристика,
        ВТ_Остатки.КоличествоОстаток КАК Остаток,
        0 КАК Резерв
    ИЗ
        ВТ_Остатки КАК ВТ_Остатки
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        ВТ_Резервы.Номенклатура,
        ВТ_Резервы.ХарактеристикаНоменклатуры,
        0,
        ВТ_Резервы.КоличествоРезерв
    ИЗ
        ВТ_Резервы КАК ВТ_Резервы) КАК Т

СГРУППИРОВАТЬ ПО
    Т.Номенклатура,
    Т.Характеристика

Рекомендации по оптимизации

Выясним, что еще можно улучшить в нашем запросе, чтобы он работал молниеносно даже на больших базах данных:

Таким образом, мы проанализировали путь от простого (и неверного) запроса к профессиональному инструменту получения данных. Использование временных таблиц в сочетании с правильной группировкой и фильтрацией в параметрах — залог стабильной работы вашей системы.

← На главную