При разработке пользовательского интерфейса в 1С часто требуется выводить в списках документов дополнительную информацию из других таблиц — например, наличие товара, статусы оплаты или отгрузки. Однако при добавлении соединений в запрос динамического списка разработчики регулярно сталкиваются с ошибкой платформы: «Обнаружено дублирование поля Ссылка».
Разберем подробно причины этого поведения платформы, проанализируем сопутствующие ошибки производительности и рассмотрим правильные варианты решения данной задачи.
В динамическом списке с заполненным свойством «Основная таблица» платформа 1С требует строгой уникальности ключевого поля (в данном случае поля Ссылка) для каждой выводимой строки. Уникальный ключ необходим СУБД и платформе для корректной работы порционного считывания данных, позиционирования курсора, условного оформления и выполнения стандартных команд (копирование, удаление, открытие формы).
Проблема возникает при выполнении соединения вида ЛЕВОЕ СОЕДИНЕНИЕ таблицы документов с физической таблицей регистра накопления или табличной частью другого документа. Связь между документом и его движениями или строками является связью «один-ко-многим». Если на один документ приходится две или более записи в связанной таблице, итоговый результат запроса вернет несколько строк с одинаковой ссылкой на документ.
Выясним, как это выглядит на практике. Ниже приведен пример запроса, вызывающего ошибку:
ВЫБРАТЬ
ДокументЗаявкаНаВозвратТоваровПоставщику.Ссылка КАК Ссылка,
ДокументЗаявкаНаВозвратТоваровПоставщику.Номер КАК Номер,
ДокументЗаявкаНаВозвратТоваровПоставщику.Дата КАК Дата,
ВЫБОР
КОГДА ТоварыНаСкладах.ВНаличии > 0
ТОГДА 100
ИНАЧЕ 0
КОНЕЦ КАК ПоступлениеТовара
ИЗ
Документ.ЗаявкаНаВозвратТоваровПоставщику КАК ДокументЗаявкаНаВозвратТоваровПоставщику
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ПО ДокументЗаявкаНаВозвратТоваровПоставщику.ДокументОснование = ТоварыНаСкладах.Регистратор.Распоряжение.ЗаявкаНаВозвратТоваровОтКлиента
Поскольку документ-основание имеет несколько записей движений в регистре ТоварыНаСкладах, один документ повторится в выборке столько раз, сколько движений зарегистрировано по нему. Платформа фиксирует повторяющиеся элементы Ссылка и выдает ошибку при открытии формы.
Обратим внимание на еще один критический момент в запросе — конструкцию разворачивания полей через несколько точек:
ТоварыНаСкладах.Регистратор.Распоряжение.ЗаявкаНаВозвратТоваровОтКлиента
Поле Регистратор в физических таблицах регистров накопления всегда имеет составной тип данных. Обращение к составному типу через точку заставляет СУБД генерировать сложные соединения (LEFT JOIN или UNION ALL) со всеми таблицами документов, которые могут выступать регистраторами для данного регистра. Добавление цепочки дальнейших обращений к реквизитам делает запрос крайне неэффективным и приводит к сильным задержкам при прокрутке и поиске в динамическом списке.
Рассмотрим по шагам основные способы исправления ошибки в зависимости от поставленной задачи.
Чтобы избежать задвоения строк, необходимо сначала сгруппировать данные регистра до одной строки на документ с помощью агрегатных функций (например, МАКСИМУМ или СУММА) во вложенном запросе, а затем соединять полученный результат с основной таблицей документов.
ВЫБРАТЬ
Заявка.Ссылка КАК Ссылка,
Заявка.Номер КАК Номер,
Заявка.Дата КАК Дата,
ЕСТЬНУЛЛ(ВложенныйЗапрос.ПоступлениеТовара, 0) КАК ПоступлениеТовара
ИЗ
Документ.ЗаявкаНаВозвратТоваровПоставщику КАК Заявка
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
Движения.Регистратор КАК Регистратор,
МАКСИМУМ(ВЫБОР КОГДА Движения.ВНаличии > 0 ТОГДА 100 ИНАЧЕ 0 КОНЕЦ) КАК ПоступлениеТовара
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
ГРУППИРОВАТЬ ПО
Движения.Регистратор
) КАК ВложенныйЗапрос
ПО Заявка.ДокументОснование = ВложенныйЗапрос.Регистратор
В / EXISTS)
Если требуется лишь установить факт наличия связанных записей или признака (например, получить 100 или 0), вместо соединения таблиц можно использовать условие проверкой вхождения ссылки в подзапрос с уникальным списком:
ВЫБРАТЬ
Заявка.Ссылка КАК Ссылка,
Заявка.Номер КАК Номер,
Заявка.Дата КАК Дата,
ВЫБОР
КОГДА Заявка.ДокументОснование В (
ВЫБРАТЬ РАЗЛИЧНЫЕ
ТоварыНаСкладах.Регистратор
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ГДЕ
ТоварыНаСкладах.ВНаличии > 0
) ТОГДА 100
ИНАЧЕ 0
КОНЕЦ КАК ПоступлениеТовара
ИЗ
Документ.ЗаявкаНаВозвратТоваровПоставщику КАК Заявка
Для высоконагруженных систем правильным архитектурным решением является отказ от сложных вычислений в динамическом списке. Рассчитанный статус или флаг записывается в специально созданный независимый регистр сведений (или реквизит документа) в момент проведения или записи документа-источника. Динамический список соединяется с этим регистром по ключу 1-к-1, что обеспечивает моментальное считывание данных без дублирования строк.