В практике разработки на платформе 1С часто возникает задача вывода дополнительных данных в отчеты, сформированные на основании документов. Типичный пример — отчет по результатам инвентаризации, в который необходимо добавить информацию о местах хранения товаров. Однако при соединении данных документа с регистрами сведений разработчики часто сталкиваются с проблемой дублирования строк и некорректной фильтрации. Рассмотрим подробно, почему это происходит и как правильно составить запрос, чтобы отчет работал корректно даже при отсутствии заполненного склада в документе.
Разберем ситуацию: в документе ИнвентаризацияТоваровНаСкладе указан список товаров. Мы хотим подтянуть для каждого товара его «место хранения» из регистра сведений МестаХраненияНоменклатуры. Проблема заключается в том, что в регистре для одного товара может быть создано несколько записей для разных складов. Если просто соединить таблицу товаров со справочником или регистром, строки в отчете начнут задваиваться — система выведет все найденные соответствия.
Проанализируем основные ошибки, которые совершают разработчики в подобных запросах:
NULL. Это полноценное значение типа «Ссылка», которое имеет свои особенности при сравнении.ГДЕ. Если мы используем ЛЕВОЕ СОЕДИНЕНИЕ, а затем в условии ГДЕ накладываем ограничение на поля присоединенной таблицы, то соединение фактически превращается во ВНУТРЕННЕЕ. В итоге, если в регистре нет записи, соответствующей условию, вся строка из документа исчезнет из отчета.Выясним причину, по которой условие Склад ЕСТЬ НЕ NULL часто не срабатывает так, как ожидает программист. В контексте баз данных NULL — это отсутствие значения. Однако в 1С поля ссылочного типа в документах почти никогда не бывают NULL (за исключением случаев обращения к несуществующим записям через точку). Если поле «Склад» в шапке документа не заполнено, оно содержит пустую ссылку данного типа.
Рассмотрим, как правильно проверять ссылку на пустоту в запросе. Вместо использования ЕСТЬ NULL, следует применять конструкцию ЗНАЧЕНИЕ:
ГДЕ
ИнвентаризацияТоваровНаСкладе.Склад <> ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)
Это позволяет серверу 1С корректно обработать типизированное пустое значение. В нашем случае, если склад в документе не указан, нам нужно либо не выводить места хранения вообще, либо выводить какое-то значение по умолчанию, но при этом сама номенклатура из отчета пропадать не должна.
Посмотрим на структуру соединения. Чтобы отчет не становился пустым при отсутствии совпадений в регистре, условие соответствия складов должно быть вынесено из блока ГДЕ непосредственно в условие связи ПО. Это критически важный момент для оптимизации запросов в 1С.
Проанализируем пример правильного построения связи:
ИЗ
Документ.ИнвентаризацияТоваровНаСкладе.Товары КАК ИнвентаризацияТовары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.МестаХраненияНоменклатуры КАК МестаХранения
ПО ИнвентаризацияТовары.Номенклатура = МестаХранения.Номенклатура
И ИнвентаризацияТовары.Ссылка.Склад = МестаХранения.Склад
При такой записи, если склад в документе и складе в регистре совпадут — мы получим место хранения. Если склад в документе пустой или в регистре нет записи для этого склада — мы всё равно увидим строку товара, но поле «Место хранения» будет содержать NULL. Это именно то поведение, которое требуется для большинства складских отчетов.
Даже если мы правильно сопоставили склады, дубли могут остаться. Это происходит, если для одной связки «Номенклатура + Склад» в регистре задано несколько мест хранения (например, товар лежит в двух разных ячейках). Чтобы гарантированно получить одну строку на одну позицию товара, мы должны использовать группировку.
Рассмотрим вариант решения с использованием вложенного запроса, где мы выбираем «максимальное» или «минимальное» место хранения для каждой позиции:
ВЫБРАТЬ
МестаХранения.Номенклатура КАК Номенклатура,
МестаХранения.Склад КАК Склад,
МАКСИМУМ(МестаХранения.МестоХранения) КАК МестоХранения
ПОМЕСТИТЬ ВТ_МестаХранения
ИЗ
РегистрСведений.МестаХраненияНоменклатуры КАК МестаХранения
СГРУППИРОВАТЬ ПО
МестаХранения.Номенклатура,
МестаХранения.Склад
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ИнвТовары.Номенклатура.Наименование,
ИнвТовары.Количество,
ЕСТЬNULL(ВТ_МестаХранения.МестоХранения, "Не указано") КАК МестоХранения
ИЗ
Документ.ИнвентаризацияТоваровНаСкладе.Товары КАК ИнвТовары
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_МестаХранения КАК ВТ_МестаХранения
ПО ИнвТовары.Номенклатура = ВТ_МестаХранения.Номенклатура
И ИнвТовары.Ссылка.Склад = ВТ_МестаХранения.Склад
ГДЕ
ИнвТовары.Ссылка = &Ссылка
В данном примере мы сначала создаем временную таблицу ВТ_МестаХранения, в которой исключаем дубли по складам с помощью функции МАКСИМУМ. Затем мы соединяем эту подготовленную таблицу с товарами из документа. Использование функции ЕСТЬNULL позволяет нам заменить пустое значение в отчете на понятный текст.
Проанализируем еще один аспект производительности — обращения к реквизитам через точку (например, Номенклатура.Наименование, Номенклатура.Код). В 1С каждое такое обращение в блоке ВЫБРАТЬ порождает скрытое левое соединение с соответствующей таблицей справочника. Если позиций в инвентаризации много, это может существенно замедлить формирование отчета.
Рекомендация: Если вам нужно вывести много полей из справочника Номенклатура, лучше один раз явно присоединить таблицу справочника в блоке ИЗ. Это сделает структуру запроса прозрачной и предсказуемой для планировщика задач SQL-сервера.
Для создания идеального отчета по инвентаризации без дублей, выполним следующие шаги:
&Ссылка).Ссылка.Склад.Номенклатура и Склад.ЛЕВОЕ СОЕДИНЕНИЕ, строго соблюдая условие связи по двум полям (товар и склад) внутри конструкции ПО.Рассмотрим финальный пример запроса, который учитывает все вышеперечисленные рекомендации:
ВЫБРАТЬ
ИнвТовары.Номенклатура КАК Номенклатура,
ИнвТовары.Ссылка.Склад КАК Склад,
ИнвТовары.Количество КАК Количество,
ИнвТовары.НомерСтроки КАК НомерСтроки
ПОМЕСТИТЬ ВТ_ТоварыДокумента
ИЗ
Документ.ИнвентаризацияТоваровНаСкладе.Товары КАК ИнвТовары
ГДЕ
ИнвТовары.Ссылка = &Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Товары.НомерСтроки КАК Номер,
Товары.Номенклатура.Код КАК Код,
Товары.Номенклатура.Наименование КАК Наименование,
Товары.Количество КАК КолВо,
ЕСТЬNULL(Места.Местахранения, "---") КАК МестоХранения
ИЗ
ВТ_ТоварыДокумента КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.МестаХраненияНоменклатуры КАК Места
ПО Товары.Номенклатура = Места.Номенклатура
И Товары.Склад = Места.Склады
УПОРЯДОЧИТЬ ПО
Номер
Важный нюанс: Если ваша конфигурация поддерживает характеристики номенклатуры, обязательно добавьте поле Характеристика в условия соединения с регистром МестаХраненияНоменклатуры. В противном случае дубли будут возникать из-за того, что система будет пытаться вывести места хранения для всех характеристик данного товара одновременно.
Таким образом, соблюдение правил работы с типами данных 1С и грамотное использование условий соединений позволяют создавать быстрые и надежные отчеты, которые корректно отражают складскую информацию без лишнего дублирования данных.