Как правильно отфильтровать основной набор данных СКД по условию из подчиненного набора?

Программист 1С v8.3 (Управляемые формы)
← На главную

При разработке отчетов на базе Системы компоновки данных (СКД) программисты часто сталкиваются с ситуацией, когда необходимо связать два набора данных. Типичный пример: первый набор получает список номенклатуры, а второй — дополнительные сведения (например, ресурсные спецификации или статусы). Проблема возникает, когда нужно вывести только те записи из первого набора, для которых во втором наборе найдено соответствие с определенным условием (например, Статус = "Действующий").

По умолчанию СКД связывает наборы данных по принципу левого соединения (Left Join). Это означает, что если в основном наборе есть запись, она обязательно попадет в отчет, даже если в подчиненном наборе данных для нее нет или они отфильтрованы. В результате в отчете появляются лишние строки с пустыми значениями из второго набора. Разберем детально, как решить эту задачу и добиться поведения, аналогичного внутреннему соединению (Inner Join).

Причина поведения системы: почему фильтр внутри набора не работает

Проанализируем ситуацию: когда мы накладываем фильтр (условие ГДЕ) внутри запроса второго (ведомого) набора данных, мы лишь ограничиваем выборку самого этого набора. Однако механизм связей СКД все равно берет каждую строку из первого набора и пытается найти для нее пару во втором. Если после применения фильтра во втором наборе данных не осталось подходящей записи, СКД просто выводит строку из первого набора с пустыми полями (NULL) вместо данных из второго набора. Именно поэтому попытки сделать фильтр внутри подчиненного набора не приводят к исчезновению всей строки из отчета.

Решение 1: Объединение наборов в один запрос (рекомендуемый способ)

Наиболее правильным и производительным способом является отказ от использования нескольких наборов данных в пользу одного запроса. Это позволяет СУБД выполнить соединение на своем уровне, что значительно быстрее. Рассмотрим, как это реализовать с помощью временных таблиц.

Допустим, ваш первый запрос (основной) достаточно сложный. Мы можем поместить его во временную таблицу, а затем соединить с данными спецификаций. Рассмотрим пример структуры такого запроса:


// Помещаем сложный запрос по номенклатуре во временную таблицу
ВЫБРАТЬ
    РегистрСведенийНоменклатура.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ОсновнаяНоменклатура
ИЗ
    РегистрСведений.Номенклатура КАК РегистрСведенийНоменклатура;

////////////////////////////////////////////////////////////////////////////////
// Соединяем с данными спецификаций, используя ВНУТРЕННЕЕ СОЕДИНЕНИЕ
ВЫБРАТЬ
    ВТ.Номенклатура,
    Спецификации.Статус
ИЗ
    ВТ_ОсновнаяНоменклатура КАК ВТ
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.РесурсныеСпецификации КАК Спецификации
        ПО ВТ.Номенклатура = Спецификации.Номенклатура
ГДЕ
    Спецификации.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыСпецификаций.Действующий)

Используя ВНУТРЕННЕЕ СОЕДИНЕНИЕ (Inner Join) непосредственно в тексте запроса, мы гарантируем, что в результат попадут только те позиции номенклатуры, у которых есть действующая спецификация. Если записей со статусом "Действующий" может быть несколько для одной номенклатуры, не забудьте добавить группировку СГРУППИРОВАТЬ ПО, чтобы избежать дублирования строк в отчете.

Решение 2: Использование общих отборов в настройках СКД

Если по каким-то причинам вы не можете объединить запросы (например, наборы данных имеют разные типы или запрос первого набора крайне велик и не подлежит изменению), можно воспользоваться механизмом общих отборов компоновки. Рассмотрим этот метод пошагово:

  1. Оставьте оба набора данных и связь между ними на вкладке "Связи наборов данных" как есть.
  2. Перейдите на вкладку "Настройки" в нижней части конструктора СКД.
  3. Выберите вкладку "Отбор".
  4. Добавьте поле Статус (которое приходит из второго набора).
  5. Установите вид сравнения Равно и значение Действующий.

Важный нюанс: Когда вы устанавливаете отбор на уровне всего отчета (или конкретной группировки) в настройках, СКД применяет этот фильтр к уже результирующей таблице после выполнения всех связей. Поскольку для строк, не прошедших фильтр во втором наборе, значение поля Статус будет равно NULL (или будет пустым), условие Статус = "Действующий" отсечет эти строки целиком. Таким образом, мы эмулируем поведение внутреннего соединения средствами компоновщика.

Решение 3: Параметры связи для оптимизации

Если записей в первом наборе данных очень много, связь наборов может работать медленно, так как 1С выполняет запросы к подчиненному набору порциями (проблема N+1). Чтобы оптимизировать этот процесс и одновременно упростить фильтрацию, можно использовать параметры связи.

Проанализируем шаги настройки:

  1. В запросе второго набора данных добавьте условие: ГДЕ Статус = &ПараметрСтатус.
  2. На вкладке "Связи наборов данных" выберите вашу связь.
  3. В колонке "Параметры" укажите ПараметрСтатус.
  4. В качестве значения передайте либо поле из первого набора, либо фиксированное значение.

Выясним причину, почему одного этого действия недостаточно: как мы уже обсуждали, это отфильтрует данные "внутри" второго набора, но основная строка останется. Поэтому данный метод обязательно нужно сочетать с "Решением 2" (общим отбором в настройках), чтобы пустые строки не попадали в итоговый результат.

Сравнение методов и выводы

Разберем, какой метод лучше выбрать в зависимости от ситуации:

1. Производительность: Безусловным лидером является Решение 1 (единый запрос). СУБД оптимизирует план выполнения и обрабатывает данные гораздо эффективнее, чем это делает сервер приложений 1С при сопоставлении коллекций в памяти.

2. Гибкость: Решение 2 (общие отборы) удобнее, если вы хотите дать пользователю возможность самому решать, фильтровать ли отчет по этому статусу или выводить все данные. Достаточно вынести этот отбор в пользовательские настройки.

3. Сложность: Иногда первый набор данных является Объектом (например, внешняя таблица значений) или результатом работы другой системы. В этом случае объединение в один запрос невозможно, и использование связей наборов данных с последующим общим отбором становится единственным вариантом.

Помните, что использование колонки Условие связи на вкладке связей не поможет отфильтровать строки. Это условие лишь определяет, нужно ли обращаться к подчиненному набору для конкретной строки, но не управляет видимостью самой строки основного набора. Всегда ориентируйтесь на объединение запросов как на приоритетный вариант архитектуры отчета.

← На главную