При создании отчетов в системе компоновки данных (СКД) начинающие разработчики часто сталкиваются с проблемой появления лишних строк (дублей). Особенно остро это проявляется, когда необходимо сопоставить данные из разных документов, например, план-фактный анализ по Заказам клиентов и Реализациям товаров и услуг. В этой статье мы подробно разберем, почему возникают дубли, как работает механизм «декартова произведения» и как правильно спроектировать запрос, чтобы данные выводились корректно.
Рассмотрим стандартную ситуацию: нам нужно вывести Номенклатуру, Количество по заказу и Количество отгруженное. В исходном запросе автора сообщения используется ПОЛНОЕ СОЕДИНЕНИЕ между табличными частями документов ЗаказКлиента.Товары и РеализацияТоваровУслуг.Товары по ссылке на заказ.
Выясним причину проблемы. Когда мы соединяем две табличные части напрямую по ключу (Заказу), 1С выполняет операцию, близкую к декартовому произведению. Если в заказе три строки с разной номенклатурой и в реализации три строки, то при соединении «по ссылке на документ» система создаст девять строк. Каждая строка из первой таблицы попытается соединиться с каждой строкой из второй. Если номенклатура совпадает не во всех строках или используется соединение без учета номенклатуры в условии ПО, мы получаем хаос в данных: ресурсы (количества) суммируются некорректно, а строки дублируются.
Важный момент: Даже если добавить условие соединения по Номенклатуре, при наличии нескольких одинаковых позиций в одном документе (например, разные характеристики или склады в разных строках), дубли все равно могут появиться. Проанализируем, как этого избежать.
Чтобы данные не множились, а дополняли друг друга, вместо соединений (JOIN) следует использовать объединение запросов (UNION ALL) в рамках одного набора данных. Рассмотрим алгоритм построения такого запроса:
ЗаказКлиента.Товары. Здесь мы заполняем поле КоличествоЗаказано, а для поля КоличествоОтгружено ставим значение 0.ВыручкаИСебестоимостьПродаж) или из РеализацияТоваровУслуг.Товары. Здесь, наоборот, для поля КоличествоЗаказано ставим 0, а поле КоличествоОтгружено заполняем реальными данными.Разберем пример кода, который реализует этот подход:
ВЫБРАТЬ
докЗаказКлиента.Ссылка КАК ЗаказКлиента,
докЗаказКлиента.Контрагент КАК Контрагент,
ЗаказКлиентаТовары.Номенклатура КАК Номенклатура,
СУММА(ЗаказКлиентаТовары.Количество) КАК КоличествоЗаказано,
0 КАК КоличествоОтгружено,
докЗаказКлиента.Дата КАК Дата
ИЗ
Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК докЗаказКлиента
ПО ЗаказКлиентаТовары.Ссылка = докЗаказКлиента.Ссылка
ГДЕ
докЗаказКлиента.Дата МЕЖДУ &ДатаНач И &ДатаКон
СГРУППИРОВАТЬ ПО
ЗаказКлиентаТовары.Номенклатура,
докЗаказКлиента.Ссылка,
докЗаказКлиента.Контрагент,
докЗаказКлиента.Дата
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ВыручкаОбороты.ЗаказКлиента,
ВыручкаОбороты.АналитикаУчетаПоПартнерам.Контрагент,
ВыручкаОбороты.АналитикаУчетаНоменклатуры.Номенклатура,
0,
ВыручкаОбороты.КоличествоОборот,
ВыручкаОбороты.ЗаказКлиента.Дата
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&ДатаНач, &ДатаКон, , ) КАК ВыручкаОбороты
После такого объединения в наборе данных окажется две строки для одной и той же номенклатуры: в одной будет заполнено только заказанное количество, в другой — только отгруженное. Теперь «магия» СКД позволит нам схлопнуть эти строки в одну.
Даже при использовании ОБЪЕДИНИТЬ ВСЕ вы можете увидеть две разные строки в отчете, если неправильно настроена структура компоновки. Посмотрим, как это исправить.
Перейдем на вкладку «Настройки» в СКД. Самая частая ошибка — использование ветки Детальные записи. В нашем случае она противопоказана, так как она выводит каждую запись из результата запроса «как есть».
Рассмотрим правильную структуру группировок:
Контрагент.ЗаказКлиента.Номенклатура.Почему это работает? На вкладке «Ресурсы» мы указали функцию СУММА() для полей количества. Когда СКД доходит до группировки по Номенклатуре, она берет все записи из запроса, у которых совпадают заказ и номенклатура, и складывает их значения. Таким образом, наши «0» из первой части объединения суммируются с реальными числами из второй части, и получается одна красивая строка.
Если вы решили использовать не один запрос с ОБЪЕДИНИТЬ ВСЕ, а механизм СКД Набор данных - объединение, вы можете столкнуться с тем, что строки все равно не соединяются. Проанализируем ситуацию: если в одном наборе поле называется Номенклатура, а в другом — Товар, СКД может воспринять их как разные измерения.
Проверьте следующие параметры:
Путь к данным.ЕстьNULL(Заказ, СсылкаНаРеализацию)), и группировать уже по нему.Для получения количества отгруженного товара крайне рекомендуется использовать не табличные части документа РеализацияТоваровУслуг, а виртуальные таблицы регистров накопления, таких как ВыручкаИСебестоимостьПродаж или ЗаказыКлиентов.
Разберем преимущества этого подхода:
ВыручкаИСебестоимостьПродаж уже есть готовое измерение ЗаказКлиента, что избавляет нас от необходимости «дотягиваться» до заказа через реквизиты реализации, что значительно ускоряет работу отчета.Проанализируем ситуацию, когда реализация сделана без заказа. Если мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ по заказу, такие продажи просто не попадут в отчет. При использовании ОБЪЕДИНИТЬ ВСЕ с обращением к регистру такие записи выведутся с пустым значением в поле ЗаказКлиента, что позволит увидеть их в общем списке как «Продажи вне заказов».
Подведем итог. Чтобы избавиться от дублей, нам необходимо:
ОБЪЕДИНИТЬ ВСЕ, разделяя выборку данных по заказам и по отгрузкам.0 (для чисел) или ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка) (для ссылок).СУММА.Детальные записи в настройках отчета на иерархическую структуру группировок (Контрагент -> Заказ -> Номенклатура).Следуя этим правилам, мы получим чистый, быстрый и лишенный дублей отчет, который корректно отображает план-фактный анализ отгрузок.