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