Как исправить ошибку «Обнаружено дублирование ключевых значений в колонках: "Ссылка"» в динамическом списке при прокрутке

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

Выясним причину возникновения ошибки

Динамический список (ДС) работает на базе Системы компоновки данных (СКД). Его главная особенность — порционное считывание данных. Для того чтобы система могла корректно отображать строки, кэшировать их и сопоставлять при прокрутке, ей необходим уникальный ключ для каждой строки. По умолчанию в качестве такого ключа используется поле Ссылка (основная таблица списка).

Проблема заключается в том, что ваш запрос на уровне базы данных (SQL) может возвращать несколько строк с одинаковым значением Ссылка. Даже если при запуске консоли запросов вам кажется, что дублей нет, они могут появиться при определенных условиях (например, когда у одного заказа появляется две оплаты или две реализации). Как только СКД при прокрутке обнаруживает в выборке две разные строки с одним и тем же идентификатором Ссылка, она выдает ошибку, так как не понимает, какую именно строку нужно отобразить в данной позиции списка.

Проанализируем проблемный запрос

Давайте разберем типичный пример запроса, который приводит к подобной ошибке (аналогичный приведенному в обсуждении темы). Рассмотрим фрагмент соединений:


ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеБезналичныхДенежныхСредств.РасшифровкаПлатежа КАК ПБДС
ПО ДокументЗаказКлиента.Ссылка = ПБДС.ОснованиеПлатежа И (ПБДС.Ссылка.Проведен)

ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ПО ДокументЗаказКлиента.Ссылка = РеализацияТоваровУслуг.ЗаказКлиента И (РеализацияТоваровУслуг.Ссылка.Проведен)

В чем здесь скрытая угроза? Проанализируем связи:

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

Важно понимать: использование ВЫБРАТЬ РАЗЛИЧНЫЕ помогает только в том случае, если все поля в выбираемых строках идентичны. Если же в одной строке сумма оплаты 100 рублей, а во второй 200, то строки будут считаться различными, и Ссылка заказа все равно продублируется.

Решение №1: Использование агрегатных функций и группировки

Самый правильный и надежный способ — гарантировать, что для каждой ссылки основного документа будет возвращена ровно одна строка. Для этого мы должны использовать СГРУППИРОВАТЬ ПО. Рассмотрим, как переписать блок с оплатами и реализациями:


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

В данном примере мы используем МАКСИМУМ для булевых полей (если есть хоть одна проведенная оплата, значение будет ИСТИНА) и СУММА для числовых показателей. Теперь на каждый заказ приходится ровно одна строка, и ошибка в динамическом списке пропадет.

Решение №2: Использование вложенных запросов (подзапросов)

Если нам нужно просто проверить наличие факта (например, «Оплачено» или «Отгружено»), эффективнее использовать коррелированные подзапросы или оператор В (ВЫБРАТЬ...). Это избавит запрос от тяжелых соединений «один ко многим».

Посмотрим на пример проверки отгрузки:


ВЫБРАТЬ
    Заказ.Ссылка,
    ВЫБОР 
        КОГДА Заказ.Ссылка В (ВЫБРАТЬ РАЗЛИЧНЫЕ Т.ЗаказКлиента ИЗ Документ.РеализацияТоваровУслуг КАК Т ГДЕ Т.Проведен) 
        ТОГДА ИСТИНА 
        ИНАЧЕ ЛОЖЬ 
    КОНЕЦ КАК Отгружен
ИЗ
    Документ.ЗаказКлиента КАК Заказ

Такой подход работает быстрее в динамических списках, так как база данных оптимизирует проверку существования записи (EXISTS) лучше, чем полное соединение таблиц.

Решение №3: Работа с дополнительными реквизитами

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


ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.ДополнительныеРеквизиты КАК ДопРеквизит
    ПО ДокументЗаказКлиента.Ссылка = ДопРеквизит.Ссылка
    И ДопРеквизит.Свойство = &КонкретноеСвойствоСсылка

Обратите внимание, что лучше передавать &КонкретноеСвойствоСсылка как параметр (ссылку на элемент справочника ДополнительныеСведенияИРеквизиты), а не искать по наименованию ("Результат отправлен на почту"). Поиск по строке ("Наименование") крайне медленный и менее надежный.

Важные рекомендации по настройке динамического списка

Помимо исправления самого текста запроса, обратим внимание на настройки в конфигураторе (или программно в расширении):

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

← На главную