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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Почему возникает эта ошибка

Для понимания сути проблемы разберем внутреннее устройство динамического списка в платформе 1С:Предприятие 8.3. Этот элемент предназначен для динамической постраничной выборки информации из базы данных. Если у динамического списка заполнено свойство Основная таблица, платформа накладывает строгое системное ограничение: ключевое поле этой таблицы (для документов и элементов справочников это поле Ссылка) обязано быть абсолютно уникальным в каждой строке результирующего набора данных.

Уникальный ключ требуется платформе для следующих операций:

  1. Обеспечение порционного считывания данных при прокрутке списка курсором вниз или вверх.
  2. Корректное позиционирование текущей строки при возврате в форму или при программной активизации строки.
  3. Кэширование считанных данных на клиенте.
  4. Корректная работа команд командной панели (проведение, отмена проведения, пометка на удаление, изменение).

Проанализируем ситуацию, описанную в теме: к списку документов займов разработчик присоединил через ЛЕВОЕ СОЕДИНЕНИЕ таблицу возвратов. Если по какому-либо займу было оформлено два и более возврата, происходит классическое размножение строк по связи «один ко многим» (1:N). В результате в результирующую выборку попадает несколько одинаковых ссылок на один и тот же займ, что мгновенно вызывает системное исключение платформы.

Способ 1. Предварительная агрегация во вложенном запросе

Рассмотрим наиболее правильный и рекомендуемый вариант решения в ситуациях, когда требуется вывести агрегированную информацию (например, общую сумму возврата или дату последней операции). Чтобы исключить размножение строк, подчиненные данные необходимо свернуть до уровня связи «один к одному» (1:1) еще до соединения с основной таблицей.

Разберем по шагам написание запроса с использованием вложенной группировки:


ВЫБРАТЬ
    ДокументЗайм.Ссылка КАК Ссылка,
    ДокументЗайм.Номер КАК Номер,
    ДокументЗайм.Дата КАК Дата,
    ДокументЗайм.СуммаДокумента КАК СуммаЗайма,
    ЕСТЬNULL(ВложенныйВозвраты.СуммаВозвращено, 0) КАК Возвращено,
    ВложенныйВозвраты.ДатаПоследнегоВозврата КАК ДатаПоследнегоВозврата
ИЗ
    Документ.Займ КАК ДокументЗайм
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            ВозвратЗайма.Займ КАК Займ,
            СУММА(ВозвратЗайма.Сумма) КАК СуммаВозвращено,
            МАКСИМУМ(ВозвратЗайма.Дата) КАК ДатаПоследнегоВозврата
        ИЗ
            Документ.ВозвратЗайма КАК ВозвратЗайма
        ГДЕ
            ВозвратЗайма.Проведен = ИСТИНА
        СГРУППИРОВАТЬ ПО
            ВозвратЗайма.Займ
    ) КАК ВложенныйВозвраты
    ПО ДокументЗайм.Ссылка = ВложенныйВозвраты.Займ

В этом примере каждая ссылка на займ встретится во вложенной выборке строго один раз, благодаря чему платформа не зафиксирует дублирования ключевых полей.

Способ 2. Группировка основного запроса

Если структура запроса не требует вынесения логики во вложенные конструкции, выполним группировку на верхнем уровне запроса. Проанализируем этот подход: все поля основной таблицы документа перечисляются в секции СГРУППИРОВАТЬ ПО, а к полям присоединенных таблиц применяются агрегатные функции СУММА, МИНИМУМ, МАКСИМУМ или КОЛИЧЕСТВО.

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


ВЫБРАТЬ
    ДокументЗайм.Ссылка КАК Ссылка,
    ДокументЗайм.Номер КАК Номер,
    ДокументЗайм.Дата КАК Дата,
    СУММА(ЕСТЬNULL(ВозвратЗайма.Сумма, 0)) КАК СуммаВозвратов
ИЗ
    Документ.Займ КАК ДокументЗайм
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратЗайма КАК ВозвратЗайма
        ПО ДокументЗайм.Ссылка = ВозвратЗайма.Займ
СГРУППИРОВАТЬ ПО
    ДокументЗайм.Ссылка,
    ДокументЗайм.Номер,
    ДокументЗайм.Дата

Важное замечание: при использовании ключевого слова РАЗЛИЧНЫЕ вместо группировки ошибка пропадет только тогда, когда все выбираемые колонки подчиненной таблицы имеют одинаковые значения. Если же в выборку включены разные номера или даты возвратов, ключевое слово РАЗЛИЧНЫЕ дублирование не устранит, так как строки в целом различаются.

Способ 3. Изменение основной таблицы динамического списка

Рассмотрим альтернативную задачу: пользователю необходимо видеть не просто займы, а именно детальный журнал всех возвратов с информацией о том, к какому займу относится каждый возврат. В этом случае правильным архитектурным решением будет смена ведущей сущности.

Разберем необходимые действия:

  1. Откроем свойства динамического списка в палитре свойств формы.
  2. В поле Основная таблица установим подчиненный документ, например Документ.ВозвратЗайма.
  3. В тексте запроса свяжем возврат с займом. Отношение станет «многие к одному» (N:1).

Поскольку ключевым полем динамического списка станет Ссылка возврата займа, каждая запись списка будет уникальной, и дублирование исключается.

Способ 4. Очистка свойства «Основная таблица»

Платформа позволяет полностью очистить поле Основная таблица в свойствах динамического списка. В этом режиме динамический список перестает отслеживать уникальность поля Ссылка и способен отображать абсолютно любые наборы данных, полученные произвольным запросом.

Однако отключение основной таблицы влечет серьезные ограничения, о которых необходимо помнить разработчику:

  1. Блокировка стандартных команд: кнопки проведения, отмены проведения, установки пометки на удаление, создания на основании и копирования перестают работать автоматически или исчезают из интерфейса.
  2. Отключение стандартных пиктограмм: в строках таблицы пропадают иконки проведения документа или отметки удаления.
  3. Снижение производительности: отключается быстрый постраничный механизм считывания СУБД, что на больших объемах данных может привести к зависанию формы при прокрутке.
  4. Запрет редактирования: интерактивное редактирование полей прямо в списке становится недоступным.

Способ 5. Построение интерфейса «Мастер — Деталь»

Если пользователю необходимо одновременно видеть и перечень займов, и детальный список всех движений или возвратов по выбранному займу, не следует объединять их в один запрос. Наиболее удобным и надежным паттерном проектирования управляемых форм является интерфейс Master-Detail.

Разберем реализацию этого паттерна:

  1. Основной динамический список (Мастер) строим только по таблице Документ.Займ без объединений с возвратами. Это гарантирует максимальное быстродействие и отсутствие дублей.
  2. Создаем второй динамический список (Деталь) по таблице Документ.ВозвратЗайма и размещаем его таблицу рядом или под основным списком.
  3. В обработчике события ПриАктивизацииСтроки первой таблицы программно устанавливаем отбор во втором динамическом списке по значению ТекущиеДанные.Ссылка.

Такой подход полностью устраняет конфликт уникальности ключей, обеспечивает максимальную скорость работы интерфейса и сохраняет всю стандартную функциональность документов 1С.

← На главную