Разработка интерфейсов на платформе 1С часто сталкивается с задачей отображения списка объектов (например, Контрагентов), которые нужно отфильтровать по данным подчиненных сущностей (например, Договоров). Сложность заключается в том, что связь между ними — «один ко многим»: у одного контрагента может быть несколько десятков договоров. Если мы просто добавим ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) в запрос динамического списка, мы получим дублирование записей основного объекта столько раз, сколько подчиненных записей найдено в базе.
В этой статье мы подробно разберем, как реализовать подобный функционал, сохранив уникальность строк в списке и обеспечив высокую производительность системы. Мы рассмотрим несколько архитектурных подходов: от простых подзапросов до использования механизмов Системы компоновки данных (СКД).
Это самый распространенный и надежный способ. Вместо того чтобы соединять таблицы, мы накладываем условие на поле Ссылка основной таблицы, проверяя её наличие в выборке подчиненного справочника. Рассмотрим ситуацию: нам нужно вывести только тех контрагентов, у которых есть хотя бы один договор с определенным типом.
Проанализируем структуру запроса в динамическом списке:
ВЫБРАТЬ
СправочникКонтрагенты.Ссылка,
СправочникКонтрагенты.Наименование,
СправочникКонтрагенты.ИНН
ИЗ
Справочник.Контрагенты КАК СправочникКонтрагенты
ГДЕ
СправочникКонтрагенты.Ссылка В
(ВЫБРАТЬ
Договоры.Владелец
ИЗ
Справочник.Договоры КАК Договоры
ГДЕ
Договоры.ТипДоговора = &НужныйТип)
Почему это работает: Оператор В в SQL (и в языке запросов 1С) работает как фильтр вхождения. Даже если у контрагента 10 договоров нужного типа, подзапрос вернет ссылку на контрагента, и основная выборка отобразит его ровно один раз. Это исключает дублирование записей в списке, которое неизбежно возникло бы при использовании соединения.
Если нам нужно, чтобы пользователь сам мог выбирать типы договоров в стандартном окне «Настроить список», мы должны использовать расширения языка запросов для СКД. Это позволяет динамическому списку «понимать», когда нужно подключать фильтр по подчиненной таблице, а когда — нет.
Рассмотрим пример настройки текста запроса:
ВЫБРАТЬ
СправочникКонтрагенты.Ссылка,
СправочникКонтрагенты.Наименование
ИЗ
Справочник.Контрагенты КАК СправочникКонтрагенты
{ГДЕ
СправочникКонтрагенты.Ссылка В
(ВЫБРАТЬ
Договоры.Владелец
ИЗ
Справочник.Договоры КАК Договоры
ГДЕ
Договоры.ТипДоговора.* КАК ТипДоговора)}
В этом случае в настройках динамического списка (на вкладке «Отбор») появится доступное поле ТипДоговора. Если пользователь не установит по нему отбор, платформа проигнорирует подзапрос и список будет работать максимально быстро. Как только флажок будет установлен, платформа подставит условие в итоговый SQL-запрос.
С точки зрения производительности на больших объемах данных (десятки и сотни тысяч записей), оператор СУЩЕСТВУЕТ часто отрабатывает эффективнее, чем В. Это связано с тем, что оптимизатор SQL-сервера прекращает поиск в подчиненной таблице, как только находит первое совпадение.
Пример реализации в тексте запроса:
ВЫБРАТЬ
Контрагенты.Ссылка
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
ЕСТЬ (ВЫБРАТЬ 1
ИЗ Справочник.Договоры КАК Договоры
ГДЕ Договоры.Владелец = Контрагенты.Ссылка
И Договоры.ТипДоговора = &Тип)
Здесь мы не выгружаем все ссылки во временную таблицу для сравнения, а проверяем наличие хотя бы одной записи, удовлетворяющей условию связи. Это классический «halal» способ решения задачи (как выразился автор темы), не требующий динамической программной модификации текста запроса.
Рассмотрим более сложный, но «продвинутый» вариант. Платформа позволяет описывать дополнительные реквизиты объектов через механизм характеристик. Если настроить Справочник.Договоры как источник характеристик для Справочника.Контрагенты, то типы договоров можно будет выбирать в отборах так, будто это реквизиты самого контрагента.
Для этого в конструкторе запроса динамического списка на закладке «Характеристики» нужно указать:
Владелец соответствует контрагенту, Вид — типу договора, а Значение — самому договору (или признаку его наличия).Этот метод позволяет пользователю строить сложнейшие фильтры (например: «Показать контрагентов, у которых есть договор типа А, но нет договора типа Б») без написания единой строки кода.
При работе с динамическими списками важно помнить о следующих нюансах:
Основная таблица (например, Справочник.Контрагенты). Это позволяет платформе использовать динамическое считывание данных (порциями) и обеспечивает работу стандартных команд: «Создать», «Копировать», «Пометка на удаление».ТипДоговора), должно быть индексировано. В противном случае при каждом обновлении списка сервер будет выполнять полное сканирование таблицы договоров, что приведет к «тормозам» интерфейса.ЛЕВОГО СОЕДИНЕНИЯ) лишает список динамичности. Платформа будет вынуждена сначала выполнить весь тяжелый запрос целиком, сгруппировать его и только потом показать первые 20 записей пользователю. На больших базах это недопустимо.Проанализировав ситуацию, мы выяснили, что оптимальным решением является использование подзапроса в секции ГДЕ. Это гарантирует уникальность записей, позволяет сохранить Основную таблицу списка и дает возможность гибко настраивать отборы через расширения СКД. Выбирайте вариант с оператором В для простоты реализации или ЕСТЬ (EXISTS) для достижения максимальной производительности на крупных проектах.