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