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