Как организовать отбор в форме списка справочника по элементам табличной части документа и избежать дублирования ссылок

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

При работе с динамическими списками в 1С на управляемых формах часто возникает задача: отфильтровать список элементов справочника по данным, которые содержатся в табличных частях связанных документов. Типичный сценарий — показать только те элементы справочника, которые выбраны в конкретных документах или содержат определенную номенклатуру в своих спецификациях. Однако при попытке реализовать это через простое соединение в запросе программисты сталкиваются с критической ошибкой платформы. Рассмотрим подробнее, почему это происходит и как правильно решить задачу несколькими способами.

Анализ причины проблемы: почему возникает дублирование «Ссылки»

Когда мы используем произвольный запрос в динамическом списке и указываем Основную таблицу (например, Справочник.Расш2_МедиаКонтент), платформа 1С ожидает, что каждая строка результата запроса будет соответствовать уникальной записи из этой таблицы. Если мы применяем ЛЕВОЕ СОЕДИНЕНИЕ с табличной частью документа (например, ЗаказКлиента.Товары), то при наличии одной и той же ссылки на элемент справочника в нескольких строках табличной части документа, этот элемент справочника продублируется в результате запроса.

Для динамического списка это недопустимая ситуация: он не может сопоставить несколько строк результата с одной уникальной ссылкой основной таблицы. В итоге пользователь видит сообщение: «Обнаружено дублирование ключевых значений в колонках: "Ссылка". Отображение данных в списке невозможно». Наша цель — изменить логику запроса так, чтобы связь с документом использовалась только для фильтрации, а не для физического соединения строк.

Решение 1: Использование вложенного запроса с оператором «В»

Проанализируем самый надежный способ — замену соединения на условие в секции ГДЕ. Вместо того чтобы «приклеивать» таблицу товаров к справочнику, мы скажем системе: «Выбери только те элементы справочника, ссылки на которые присутствуют в списке ссылок из нужных нам документов».

Рассмотрим пример запроса для динамического списка:


ВЫБРАТЬ
    СправочникМедиа.Ссылка КАК Ссылка,
    СправочникМедиа.Код КАК Код,
    СправочникМедиа.Наименование КАК Наименование,
    СправочникМедиа.Менеджер КАК Менеджер
ИЗ
    Справочник.Расш2_МедиаКонтент КАК СправочникМедиа
ГДЕ
    СправочникМедиа.Ссылка В (
        ВЫБРАТЬ РАЗЛИЧНЫЕ
            ТЧ_Товары.СсылкаНаМедиаКонтент
        ИЗ
            Документ.ЗаказКлиента.Товары КАК ТЧ_Товары
        ГДЕ
            ТЧ_Товары.Номенклатура = &ОтборНоменклатура
    )

Использование ключевого слова РАЗЛИЧНЫЕ во вложенном запросе гарантирует, что список ссылок будет уникальным, и основная выборка не «размножится». Этот метод отлично подходит, если вам нужно жестко ограничить список при открытии или по конкретному параметру.

Решение 2: Применение оператора «СУЩЕСТВУЕТ» (EXISTS)

С точки зрения производительности СУБД (особенно на больших объемах данных), использование оператора СУЩЕСТВУЕТ часто оказывается эффективнее, чем оператор В. Выясним, как переписать наш запрос под эту логику. В этом случае мы проверяем наличие хотя бы одной записи в табличной части документа, удовлетворяющей условию связи со справочником.


ВЫБРАТЬ
    СправочникМедиа.Ссылка КАК Ссылка,
    СправочникМедиа.Наименование КАК Наименование
ИЗ
    Справочник.Расш2_МедиаКонтент КАК СправочникМедиа
ГДЕ
    СУЩЕСТВУЕТ (
        ВЫБРАТЬ 1
        ИЗ
            Документ.ЗаказКлиента.Товары КАК ЗаказТовары
        ГДЕ
            ЗаказТовары.СсылкаНаМедиаКонтент = СправочникМедиа.Ссылка
            И ЗаказТовары.Номенклатура = &Номенклатура
    )

Такой подход исключает дублирование на корню, так как вложенный запрос возвращает истину или ложь для каждой строки справочника, не создавая новых строк в итоговой выборке.

Решение 3: Группировка данных (GROUP BY)

Если по каким-то причинам вам всё же необходимо использовать ЛЕВОЕ СОЕДИНЕНИЕ (например, вы хотите выводить в колонки списка какие-то агрегированные данные из документов — общую сумму или количество упоминаний), то единственным выходом будет использование группировки. Посмотрим на пример:


ВЫБРАТЬ
    СправочникМедиа.Ссылка КАК Ссылка,
    СправочникМедиа.Наименование КАК Наименование,
    МАКСИМУМ(ЗаказТовары.Ссылка.Дата) КАК ДатаПоследнегоЗаказа
ИЗ
    Справочник.Расш2_МедиаКонтент КАК СправочникМедиа
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.Товары КАК ЗаказТовары
    ПО СправочникМедиа.Ссылка = ЗаказТовары.СсылкаНаМедиаКонтент
СГРУППИРОВАТЬ ПО
    СправочникМедиа.Ссылка,
    СправочникМедиа.Наименование

Важный нюанс: при использовании группировки в динамических списках может снизиться скорость прокрутки списка, так как серверу базы данных приходится выполнять тяжелые вычисления для каждой страницы данных.

Решение 4: Использование «Критериев отбора»

В платформе 1С существует специальный объект метаданных — Критерий отбора. Разберем, как он может помочь. Если вы создадите критерий отбора (например, «ПоМедиаКонтенту») и в его составе укажете табличную часть ЗаказКлиента.Товары (поле, где хранится ссылка на ваш справочник), то платформа автоматически создаст оптимальный механизм поиска связей.

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

Решение 5: Программная установка отбора при открытии формы

Если ваша задача — открыть форму списка справочника из формы документа с уже установленным фильтром по табличной части, можно воспользоваться штатным механизмом параметров формы. Рассмотрим пример кода на встроенном языке:


&НаКлиенте
Процедура ОткрытьСписокСОтбором()
    // Формируем структуру отбора по реквизиту документа, связанного со справочником
    ПараметрыОтбора = Новый Структура("Номенклатура", Объект.Товары[0].Номенклатура);
    
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("Отбор", ПараметрыОтбора);
    
    ОткрытьФорму("Справочник.Расш2_МедиаКонтент.ФормаСписка", ПараметрыФормы);
КонецПроцедуры

Этот способ работает «из коробки», если в динамическом списке формы справочника настроены соответствующие связи или если поле отбора является реквизитом самого справочника.

Рекомендации по оптимизации

  1. Индексация полей: При выполнении отборов по табличным частям крайне важно, чтобы поле в табличной части (в нашем примере СсылкаНаМедиаКонтент) имело свойство Индексировать. Без этого поиск по документам будет приводить к полному сканированию таблиц, что парализует работу пользователей в большой базе.
  2. Пользовательские настройки: Чтобы пользователь мог сам менять значение отбора (например, выбирать номенклатуру прямо в форме списка), не забудьте включить созданный параметр запроса в Пользовательские настройки динамического списка в конструкторе компоновки данных.
  3. Основная таблица: Никогда не очищайте поле «Основная таблица» в настройках динамического списка только ради того, чтобы убрать ошибку дублирования. Это лишит вашу форму стандартных кнопок («Создать», «Удалить», «Изменить») и сделает её неудобной для работы. Используйте методы с вложенными запросами, описанные выше.

Проанализировав ситуацию, мы видим, что проблема дублирования в 1С — это лишь сигнал о некорректно выбранном методе связи данных. Используя СУЩЕСТВУЕТ или вложенные запросы В (ВЫБРАТЬ...), мы сохраняем целостность динамического списка и обеспечиваем пользователю быстрый и удобный поиск нужных элементов.

← На главную