При разработке интерфейсов на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с загадочной ошибкой в динамических списках: «Обнаружено дублирование ключевых значений в колонках: "Ссылка1". Отображение данных в списке невозможно». Самое коварное в этой ситуации то, что при проверке того же самого запроса в консоли запросов данные могут выглядеть абсолютно корректно и дублей не содержать. Давайте подробно разберем, откуда берется это мистическое поле Ссылка1 и как нам совместно устранить эту проблему.
Для начала проанализируем внутренние механизмы платформы. Когда мы настраиваем динамический список с произвольным запросом и указываем Основную таблицу, система берет на себя управление выборкой данных, навигацией и выделением строк. Для этого ей необходим уникальный идентификатор каждой строки. Платформа автоматически добавляет в итоговый SQL-запрос поле уникального идентификатора.
Если в нашем запросе уже присутствует поле с псевдонимом Ссылка, или если платформа при автоматической генерации технического запроса сталкивается с конфликтом имен, она присваивает внутреннему идентификатору псевдоним Ссылка1. Ошибка возникает тогда, когда в результате соединений (JOIN) в итоговую выборку попадают две или более строки с одинаковым значением в этом ключевом поле. Система теряет возможность однозначно идентифицировать строку в списке, что и приводит к остановке вывода данных.
Первым делом нам необходимо проверить настройки самого динамического списка в конфигураторе. Рассмотрим свойства объекта ДинамическийСписок. Поле Основная таблица должно быть заполнено правильно. Если мы работаем с журналом документов, там должна быть указана таблица журнала, например ЖурналДокументов.ЖурналУведомленийWMS.
Если же мы сознательно не указываем основную таблицу (например, при объединении нескольких несвязанных источников), нам крайне важно убедиться, что в колонке Ключ объекта выбрано поле, которое гарантированно уникально для каждой строки. Но помните: в 90% случаев проблема кроется именно в неявном размножении записей из-за левых соединений.
Рассмотрим ситуацию, когда в консоли запросов все чисто, а список выдает ошибку. Это происходит потому, что консоль выводит все записи сразу, а динамический список работает порциями. Чтобы найти «виновника», проанализируем структуру наших соединений. Чаще всего дубли вызывают соединения вида «один ко многим» (1:N).
Например, если мы присоединяем к журналу документов таблицу Документ.РеализацияТоваровУслуг по полю ЗаказКлиента, а на один заказ было оформлено две реализации, то строка журнала продублируется. Выясним это с помощью специального диагностического запроса:
ВЫБРАТЬ
Журнал.Ссылка КАК Ссылка,
1 КАК Количество
ПОМЕСТИТЬ ВТ_Данные
ИЗ
ЖурналДокументов.ЖурналУведомленийWMS КАК Журнал
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Реализация
ПО (ВЫРАЗИТЬ(Журнал.ДокументОснование КАК Документ.ЗаказКлиента) = Реализация.ЗаказКлиента)
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусОбработкиУведомленияСкладом КАК Статусы
ПО (Журнал.Ссылка = Статусы.Уведомление);
//////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Ссылка КАК Ссылка,
СУММА(ВТ.Количество) КАК КолВоЗаписей
ИЗ
ВТ_Данные КАК ВТ
СГРУППИРОВАТЬ ПО
ВТ.Ссылка
ИМЕЮЩИЕ
СУММА(ВТ.Количество) > 1
Если этот запрос вернул хотя бы одну строку — мы нашли причину. Каждое такое уведомление будет вызывать ошибку в динамическом списке.
Теперь разберем, как нам исправить логику запроса. Если нам нужно вывести информацию, которая может дублироваться (например, номер реализации), у нас есть два пути:
РеализацияТоваровУслуг, проанализируем возможность использования конструкции ВЫБРАТЬ ПЕРВЫЕ 1 или агрегатных функций.Также обратим внимание на корректность типов данных. В исходном запросе автора была допущена ошибка типизации ISNULL:
ЕСТЬNULL(РеализацияТоваровУслуг.Ссылка, "")
Так делать не рекомендуется. Платформа может некорректно обрабатывать смешанные типы (Ссылка и Строка) при построении индекса для динамического списка. Правильнее будет использовать пустую ссылку того же типа:
ЕСТЬNULL(РеализацияТоваровУслуг.Ссылка, ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка))
Бывает так, что запрос идеален, но ошибка остается. Проанализируем влияние RLS (Row Level Security). Механизм ограничения прав доступа на уровне записей неявно модифицирует ваш запрос перед отправкой на SQL-сервер. Если в правилах RLS прописаны сложные условия с соединениями, они могут добавлять дубли, которые не видны программисту под полными правами. Обязательно протестируйте список под учетной записью обычного пользователя.
Другой важный момент — настройки компоновки данных. Динамические списки в управляемых формах работают на базе СКД. Пользователь мог установить отбор или группировку по реквизиту, который заставляет систему присоединять дополнительные таблицы. Попробуем выполнить следующие действия:
При работе с журналами документов (как в нашем примере) важно помнить, что это виртуальная таблица. Если мы соединяем журнал с физической таблицей документа, входящего в этот журнал, риск конфликта имен Ссылка крайне велик.
Рассмотрим совет опытных разработчиков: если динамический список на базе журнала с соединениями продолжает «сыпаться», попробуем заменить запрос на ОБЪЕДИНЕНИЕ ВСЕХ (UNION ALL) по всем типам документов, входящих в журнал. Это позволит жестко типизировать каждое соединение и избежать ситуации, когда платформа путается в метаданных составного типа ДокументОснование.
Подведем итог нашему исследованию. Для решения проблемы дублирования Ссылка1 мы должны:
"" в ISNULL на пустые ссылки соответствующих типов ЗНАЧЕНИЕ(...).РАЗЛИЧНЫЕ (DISTINCT) в динамических списках, так как это может сильно снизить производительность и не всегда спасает от дублей, создаваемых механизмом RLS.Соблюдение этих шагов позволит вам создать стабильный и быстрый интерфейс, а ошибка Ссылка1 больше не будет препятствием в вашей работе.