Как отфильтровать результат вычисления в запросе 1С и почему не работает конструкция ИМЕЮЩИЕ?

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

При разработке отчетов и обработок в 1С:Предприятии 8.3 часто возникает задача отобрать только те строки, в которых вычисленная разница между двумя полями превышает определенное значение. Например, требуется выявить строки заказа клиента, по которым количество в заказе превышает свободный остаток на складе. Типичной ошибкой начинающих разработчиков становится попытка применить конструкцию ИМЕЮЩИЕ без группировки.

Разберем детально, почему возникает ошибка при использовании ИМЕЮЩИЕ, как правильно перенести условие в секцию ГДЕ, и какие важные архитектурные нюансы работы с остатками и значениями NULL необходимо учитывать.

Причина ошибки: ограничения секции ИМЕЮЩИЕ (HAVING)

Выясним причину, по которой конструкция ИМЕЮЩИЕ не срабатывает в исходном запросе. В языке запросов 1С (соответствующем стандартам SQL) секция ИМЕЮЩИЕ предназначена исключительно для фильтрации результатов группировки. Для её работы критически важно выполнение двух условий:

  1. В запросе обязательно должна присутствовать секция СГРУППИРОВАТЬ ПО.
  2. Условие в секции ИМЕЮЩИЕ должно проверять агрегатные функции, такие как СУММА(), КОЛИЧЕСТВО(), МИНИМУМ() или МАКСИМУМ().

Если запрос выбирает детальные записи без группировки, синтаксис ИМЕЮЩИЕ вызовет ошибку на этапе компиляции запроса. Для условий по отдельным полям строк или их разностям предназначена секция ГДЕ.

Способ 1. Перенос условия в секцию ГДЕ

Проанализируем простейшее решение задачи. Если нам не требуется группировать строки, условие фильтрации разности полей переносится напрямую в секцию ГДЕ. Математическое выражение можно записать как в виде разности, так и через прямое сравнение полей:


ГДЕ
    (ЗаказКлиентаТовары.Количество - СвободныеОстаткиОстатки.ВНаличииОстаток) > 0

Или в более лаконичной форме:


ГДЕ
    ЗаказКлиентаТовары.Количество > СвободныеОстаткиОстатки.ВНаличииОстаток

Это полностью решает проблему фильтрации строк без необходимости усложнять запрос лишними группировками или подзапросами.

Способ 2. Использование ЛЕВОГО СОЕДИНЕНИЯ и функции ЕСТЬNULL

Рассмотрим важную деталь запроса: в исходном коде использовалось ВНУТРЕННЕЕ СОЕДИНЕНИЕ с таблицей остатков. Такое соединение отсекает позиции, по которым на складе остатка нет вообще (в регистре нет записи). В ситуации, когда свободный остаток равен нулю или отсутствует, позиция заказа выпадет из результата, хотя дефицит по ней является стопроцентным.

Чтобы избежать потери строк, необходимо использовать ЛЕВОЕ СОЕДИНЕНИЕ и обернуть поле остатка в функцию ЕСТЬNULL(), заменяя отсутствующие данные нулем:


ВЫБРАТЬ
    ЗаказКлиентаТовары.Номенклатура,
    ЗаказКлиентаТовары.Ссылка КАК Регистратор,
    ЗаказКлиентаТовары.Ссылка КАК ЗаказКлиента,
    ЗаказКлиентаТовары.Характеристика,
    ЗаказКлиентаТовары.Поставщик КАК Поставщик,
    &Период,
    ЗаказКлиентаТовары.ЦенаПоставщика,
    ЗаказКлиентаТовары.Количество - ЕСТЬNULL(СвободныеОстаткиОстатки.ВНаличииОстаток, 0) КАК Поле1,
    ЗаказКлиентаТовары.Количество
ИЗ
    Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СвободныеОстатки.Остатки(&Период, ) КАК СвободныеОстаткиОстатки
        ПО ЗаказКлиентаТовары.Номенклатура = СвободныеОстаткиОстатки.Номенклатура
        И ЗаказКлиентаТовары.Характеристика = СвободныеОстаткиОстатки.Характеристика
        И ЗаказКлиентаТовары.Склад = СвободныеОстаткиОстатки.Склад
ГДЕ
    ЗаказКлиентаТовары.Поставщик <> ЗНАЧЕНИЕ(Справочник.Партнеры.ПустаяСсылка)
    И ЗаказКлиентаТовары.Ссылка = &ЗаказКлиентаСсылка
    И НЕ ЗаказКлиентаТовары.ВариантОбеспечения В (&ВариантОбеспечения)
    И (ЗаказКлиентаТовары.Количество - ЕСТЬNULL(СвободныеОстаткиОстатки.ВНаличииОстаток, 0)) > 0
УПОРЯДОЧИТЬ ПО
    Поставщик

Оптимизация и устранение логических ошибок

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

  1. Проблема дублирования строк: Если одна и та же номенклатура указана в заказе несколькими строками, прямое соединение сравнит каждую строку с полным остатком. Например, при остатке 7 шт. и двух строках по 5 шт. условие дефицита не сработает ни для одной строки (5 < 7), хотя суммарно требуется 10 шт. Чтобы расчет был корректным, данные табличной части предварительно сворачивают с суммированием количества.
  2. Параметры виртуальной таблицы: При обращении к таблице РегистрНакопления.СвободныеОстатки.Остатки без параметров платформа рассчитывает остаток на актуальную точку времени. Если в запросе передается &Период, его обязательно передают в первый параметр таблицы: Остатки(&Период, ). Фильтры по номенклатуре и складам также рекомендуется передавать во второй параметр виртуальной таблицы для ускорения работы СУБД.
  3. Разыменование ссылок через точку: Конструкция НЕ ЗаказКлиентаТовары.Поставщик.Ссылка ЕСТЬ NULL порождает скрытое соединение с таблицей базы данных. Правильнее проверять реквизит напрямую через сравнение с пустой ссылкой: ЗаказКлиентаТовары.Поставщик <> ЗНАЧЕНИЕ(Справочник.Партнеры.ПустаяСсылка).

Таким образом, когда требуется наложить условие на вычисляемое поле без агрегации, следует использовать блок ГДЕ, применять ЛЕВОЕ СОЕДИНЕНИЕ и корректно обрабатывать поля регистра функцией ЕСТЬNULL.

← На главную