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