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

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

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

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

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

Ошибка дублирования возникает в тот момент, когда результат запроса динамического списка возвращает две и более строки с одинаковым значением в колонке «Ссылка». Чаще всего это происходит при использовании оператора ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN). Если к основной таблице (например, «Поступление товаров») присоединяется подчиненная таблица (например, «Приходные ордера») и для одного поступления в базе существует несколько ордеров, то в итоговой выборке одна и та же накладная повторится столько раз, сколько подчиненных документов к ней привязано.

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

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


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ДокументПоступлениеТоваров.Ссылка,
    ДокументПоступлениеТоваров.Номер,
    ДокументПоступлениеТоваров.Дата,
    ПриходныйОрдерНаТовары.Ссылка КАК ПриходныйОрдерНаТоварыСсылка,
    УстановкаЦенНоменклатуры.Ссылка КАК УстановкаЦенНоменклатурыСсылка
ИЗ
    Документ.ПоступлениеТоваров КАК ДокументПоступлениеТоваров
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходныйОрдерНаТовары КАК ПриходныйОрдерНаТовары
        ПО ДокументПоступлениеТоваров.Ссылка = ПриходныйОрдерНаТовары.ДокументОснование
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.УстановкаЦенНоменклатуры КАК УстановкаЦенНоменклатуры
        ПО ДокументПоступлениеТоваров.Ссылка = УстановкаЦенНоменклатуры.ДокументОснование

Почему ВЫБРАТЬ РАЗЛИЧНЫЕ (DISTINCT) здесь не помогает? Дело в том, что ключевое слово РАЗЛИЧНЫЕ проверяет уникальность всей строки целиком. Если для одного поступления есть два разных приходных ордера, то колонки ПриходныйОрдерНаТоварыСсылка будут содержать разные значения. Следовательно, строки в выборке будут считаться разными, но колонка Ссылка (основная) будет дублироваться. Это и приводит к программному сбою.

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

Самый правильный и производительный способ решения проблемы — это использование предложения СГРУППИРОВАТЬ ПО. Мы должны сгруппировать результат по всем полям основного документа, а для присоединенных данных выбрать какое-то одно значение (например, максимальную ссылку или количество).

Разберем по шагам, как переписать запрос:

  1. Перечисляем все поля основного документа в секции СГРУППИРОВАТЬ ПО.
  2. Для полей из присоединенных таблиц используем агрегатную функцию МАКСИМУМ.
  3. Для визуальной индикации (есть документ или нет) используем проверку на NULL.

Пример исправленного запроса:


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

Важный момент: Если ваша задача — просто подкрасить строку цветом (например, выделить поступления без ордеров), вам достаточно знать сам факт наличия связи. В этом случае в ВЫБРАТЬ можно использовать конструкцию КОЛИЧЕСТВО(ПриходныйОрдерНаТовары.Ссылка) > 0.

Способ 2: Соединение с вложенным запросом

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


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

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

Способ 3: Коррелированные подзапросы в списке полей

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


ВЫБРАТЬ
    ДокументПоступлениеТоваров.Ссылка,
    (ВЫБРАТЬ ПЕРВЫЕ 1 Т.Ссылка 
     ИЗ Документ.ПриходныйОрдерНаТовары КАК Т 
     ГДЕ Т.ДокументОснование = ДокументПоступлениеТоваров.Ссылка) КАК ОрдерСсылка
ИЗ
    Документ.ПоступлениеТоваров КАК ДокументПоступлениеТоваров

Хотя этот метод кажется простым, следует помнить о производительности. На очень больших списках такие подзапросы могут замедлять работу, так как СУБД придется выполнять их для каждой видимой строки списка.

Дополнительные рекомендации

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

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

← На главную