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

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

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

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

Разберем ситуацию: в документе ИнвентаризацияТоваровНаСкладе указан список товаров. Мы хотим подтянуть для каждого товара его «место хранения» из регистра сведений МестаХраненияНоменклатуры. Проблема заключается в том, что в регистре для одного товара может быть создано несколько записей для разных складов. Если просто соединить таблицу товаров со справочником или регистром, строки в отчете начнут задваиваться — система выведет все найденные соответствия.

Проанализируем основные ошибки, которые совершают разработчики в подобных запросах:

  1. Неправильная проверка на пустое значение. В языке запросов 1С пустая ссылка на объект (например, на склад) не является значением NULL. Это полноценное значение типа «Ссылка», которое имеет свои особенности при сравнении.
  2. Фильтрация по правой таблице в блоке ГДЕ. Если мы используем ЛЕВОЕ СОЕДИНЕНИЕ, а затем в условии ГДЕ накладываем ограничение на поля присоединенной таблицы, то соединение фактически превращается во ВНУТРЕННЕЕ. В итоге, если в регистре нет записи, соответствующей условию, вся строка из документа исчезнет из отчета.
  3. Игнорирование структуры регистра. Если в одном складе для одной номенклатуры предусмотрено несколько ячеек хранения, простым соединением проблему дублей не решить — нужно использовать агрегатные функции.

Разбираем понятие «Пустая ссылка» против NULL

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

Рассмотрим, как правильно проверять ссылку на пустоту в запросе. Вместо использования ЕСТЬ NULL, следует применять конструкцию ЗНАЧЕНИЕ:


ГДЕ
    ИнвентаризацияТоваровНаСкладе.Склад <> ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)

Это позволяет серверу 1С корректно обработать типизированное пустое значение. В нашем случае, если склад в документе не указан, нам нужно либо не выводить места хранения вообще, либо выводить какое-то значение по умолчанию, но при этом сама номенклатура из отчета пропадать не должна.

Правильная логика левого соединения (LEFT JOIN)

Посмотрим на структуру соединения. Чтобы отчет не становился пустым при отсутствии совпадений в регистре, условие соответствия складов должно быть вынесено из блока ГДЕ непосредственно в условие связи ПО. Это критически важный момент для оптимизации запросов в 1С.

Проанализируем пример правильного построения связи:


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

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

Борьба с дублями: использование агрегатных функций

Даже если мы правильно сопоставили склады, дубли могут остаться. Это происходит, если для одной связки «Номенклатура + Склад» в регистре задано несколько мест хранения (например, товар лежит в двух разных ячейках). Чтобы гарантированно получить одну строку на одну позицию товара, мы должны использовать группировку.

Рассмотрим вариант решения с использованием вложенного запроса, где мы выбираем «максимальное» или «минимальное» место хранения для каждой позиции:


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

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

В данном примере мы сначала создаем временную таблицу ВТ_МестаХранения, в которой исключаем дубли по складам с помощью функции МАКСИМУМ. Затем мы соединяем эту подготовленную таблицу с товарами из документа. Использование функции ЕСТЬNULL позволяет нам заменить пустое значение в отчете на понятный текст.

Оптимизация обращений через точку

Проанализируем еще один аспект производительности — обращения к реквизитам через точку (например, Номенклатура.Наименование, Номенклатура.Код). В 1С каждое такое обращение в блоке ВЫБРАТЬ порождает скрытое левое соединение с соответствующей таблицей справочника. Если позиций в инвентаризации много, это может существенно замедлить формирование отчета.

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

Итоговый алгоритм построения запроса

Для создания идеального отчета по инвентаризации без дублей, выполним следующие шаги:

  1. Определяем параметры: Передаем в запрос ссылку на конкретный документ инвентаризации (&Ссылка).
  2. Работаем со складом: Учитываем, что склад может быть в шапке документа или в каждой строке табличной части (в зависимости от конфигурации). Обращаемся к нему через Ссылка.Склад.
  3. Предварительная выборка: Если данных много, выбираем товары документа во временную таблицу и индексируем её по полям Номенклатура и Склад.
  4. Соединение с регистром: Используем ЛЕВОЕ СОЕДИНЕНИЕ, строго соблюдая условие связи по двум полям (товар и склад) внутри конструкции ПО.
  5. Обработка пустых складов: Если склад в документе не заполнен (равен пустой ссылке), соединение с регистром по полю «Склад» не вернет результат, и это корректно — в отчете просто не будет выведено место хранения, но сами товары останутся.

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


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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Товары.НомерСтроки КАК Номер,
    Товары.Номенклатура.Код КАК Код,
    Товары.Номенклатура.Наименование КАК Наименование,
    Товары.Количество КАК КолВо,
    ЕСТЬNULL(Места.Местахранения, "---") КАК МестоХранения
ИЗ
    ВТ_ТоварыДокумента КАК Товары
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.МестаХраненияНоменклатуры КАК Места
    ПО Товары.Номенклатура = Места.Номенклатура
    И Товары.Склад = Места.Склады
УПОРЯДОЧИТЬ ПО
    Номер

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

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

← На главную