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