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