Как правильно объединить остатки из нескольких регистров в запросе 1С, чтобы учитывать нулевые значения

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

При разработке отчетов в 1С часто возникает задача собрать данные из разных источников в одну таблицу. Типичный пример — отчет, который должен показать остатки товаров на складах, товары в резерве и ожидаемые заказы поставщикам. Часто начинающие разработчики пытаются решить эту задачу с помощью ПОЛНОЕ СОЕДИНЕНИЕ, но сталкиваются с тем, что данные «пропадают» или отображаются некорректно, если в одной из таблиц остаток равен нулю. Разберем, почему так происходит и как построить запрос правильно.

Проблема использования ПОЛНОГО СОЕДИНЕНИЯ и условий ГДЕ

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

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

Важное правило: никогда не используйте фильтры по полям таблиц в секции ГДЕ при использовании левых или полных соединений, если вы не планируете сознательно отсекать NULL.

Решение №1: Перенос условий в параметры виртуальных таблиц

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

Вместо того чтобы писать ГДЕ в конце запроса, мы прописываем условия внутри скобок виртуальной таблицы. Это не только исправляет логическую ошибку с NULL, но и значительно ускоряет работу запроса, так как СУБД получает из базы уже отфильтрованный набор данных.

Пример правильной настройки параметров:


РегистрНакопления.ТоварыНаСкладах.Остатки(
    &Дата2,
    Номенклатура.НоменклатурнаяГруппа В ИЕРАРХИИ(&НоменклатурнаяГруппа)
) КАК ТоварыНаСкладахОстатки

Решение №2: Метод ОБЪЕДИНИТЬ ВСЕ (Рекомендуемый)

Несмотря на то что соединение можно заставить работать с помощью функций ЕСТЬNULL, профессиональным стандартом в 1С для таких задач является использование конструкции ОБЪЕДИНИТЬ ВСЕ. Этот метод гораздо производительнее и надежнее, так как исключает риск возникновения «декартова произведения» (дублирования строк) при ошибках в условиях связи.

Разберем алгоритм построения такого запроса по шагам:

  1. Сбор данных из первого источника. Выбираем поля Склад, Номенклатура и ресурс КоличествоОстаток. Для ресурсов из других таблиц (резерв, заказы) в этом подзапросе проставляем 0.
  2. Объединение со вторым источником. Используем ОБЪЕДИНИТЬ ВСЕ. Выбираем те же измерения (Склад, Номенклатура), но теперь в поле КоличествоОстаток ставим 0, а в поле КоличествоРезерв — реальное значение из таблицы резервов.
  3. Итоговая группировка. Помещаем результат объединения во временную таблицу или во вложенный запрос и группируем по измерениям, суммируя ресурсы.

Посмотрим на пример реализации такого подхода:


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

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

Почему это лучше, чем ПОЛНОЕ СОЕДИНЕНИЕ?

Проанализируем преимущества метода объединения. Во-первых, нам не нужно использовать громоздкие конструкции ЕСТЬNULL(Таблица1.Поле, ЕСТЬNULL(Таблица2.Поле, Таблица3.Поле)) для получения корректного названия склада или номенклатуры. В методе ОБЪЕДИНИТЬ ВСЕ все ключевые поля просто выстраиваются в одну колонку.

Во-вторых, производительность. СУБД гораздо проще выполнить несколько простых выборок и одну итоговую группировку, чем вычислять сложные планы соединений для нескольких таблиц по составным условиям (Склад + Номенклатура). При больших объемах данных (десятки тысяч записей) разница в скорости может быть десятикратной.

Работа с иерархией и итогами

Выясним причину, по которой у автора возникли сложности с иерархической сортировкой. В 1С использование ИТОГИ ПО ... ИЕРАРХИЯ накладывает определенные ограничения. Если вы используете в поле итогов результат функции, например ЕСТЬNULL, система не сможет построить иерархическое дерево, так как для этого ей нужна прямая ссылка на объект из базы данных.

При использовании метода ОБЪЕДИНИТЬ ВСЕ в итоговой выборке у вас всегда будет «чистая» ссылка на Номенклатура, что позволит без проблем использовать конструкцию:


ИТОГИ
    СУММА(КоличествоОстаток),
    СУММА(КоличествоРезерв)
ПО
    Склад,
    Номенклатура ИЕРАРХИЯ

Использование временных таблиц для оптимизации

Для повышения стабильности работы и читаемости кода рекомендуется использовать механизм временных таблиц (МенеджерВременныхТаблиц). Проанализируем, как это сделать правильно:

  1. Выбираем данные из каждого регистра в отдельную временную таблицу (например, ВТ_Остатки, ВТ_Резервы).
  2. На этом этапе можно сразу рассчитать необходимые поля и наложить фильтры.
  3. В финальном запросе объединяем данные из временных таблиц.

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

Заключение

Мы рассмотрели, как избежать типичных ошибок при работе с остатками. Если вам нужно собрать данные из нескольких регистров, забудьте про ПОЛНОЕ СОЕДИНЕНИЕ. Используйте параметры виртуальных таблиц для фильтрации и ОБЪЕДИНИТЬ ВСЕ для сборки данных. Это гарантирует, что нулевые остатки не «съедят» полезную информацию, а ваш отчет будет работать быстро и выдавать достоверные цифры.

← На главную