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