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

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

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

Механизм работы динамического списка и природа ошибки

Для понимания причин сбоя проанализируем внутренние механизмы платформы 1С:

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

Поиск причин задвоения в тексте запроса

Посмотрим на типовой пример запроса динамического списка с соединениями:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    СправочникНоменклатура.Ссылка КАК Ссылка,
    СправочникНоменклатура.Наименование КАК Наименование,
    СправочникНоменклатура.Артикул КАК Артикул,
    ЕСТЬNULL(ХозрасчетныйОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
    ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) КАК Цена
ИЗ
    Справочник.Номенклатура КАК СправочникНоменклатура
    {ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних({(&ДатаОстаткиЦены)}, {(Номенклатура).* КАК Ссылка, (ТипЦен) КАК ТипЦен}) КАК ЦеныНоменклатурыСрезПоследних
        ПО СправочникНоменклатура.Ссылка = ЦеныНоменклатурыСрезПоследних.Номенклатура}
    {ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.Хозрасчетный.Остатки({(&ДатаОстаткиЦены)}, Счет В (&СписокСчетов), , Организация В (&СписокОрганизаций)) КАК ХозрасчетныйОстатки
        ПО СправочникНоменклатура.Ссылка = ХозрасчетныйОстатки.Субконто1}
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления._Доработка_УчетВесовойНоменклатуры.Остатки КАК _Доработка_УчетВесовойНоменклатурыОстатки
        ПО СправочникНоменклатура.Ссылка = _Доработка_УчетВесовойНоменклатурыОстатки.Номенклатура
ГДЕ
    НЕ СправочникНоменклатура.ЭтоГруппа

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

  1. Соединение с регистром остатков без полного отбора по измерениям: если в регистре _Доработка_УчетВесовойНоменклатуры присутствуют измерения (например, Склад, Организация или Характеристика), то виртуальная таблица Остатки вернет по одной номенклатуре несколько строк с остатками по каждому складу. Соединение ПО Номенклатура приведет к размножению строк исходного справочника.
  2. Коллизии в срезе последних периодических регистров сведений: в регистре ЦеныНоменклатуры с периодичностью «В пределах дня» или «Позиция регистратора» могут существовать записи по разным типам цен или даже несколько установок цен за один день, если отбор по типу цен или характеристике не был жестко задан в параметрах среза.
  3. Влияние условий СКД: фигурные скобки {} в запросе динамического списка модифицируют итоговый SQL-запрос на уровне Системы компоновки данных. Если пользователь в форме не задал отбор по конкретному складу или типу цен, условие соединения расширяется, и выборка теряет однозначность «1 к 1».

Методика локализации проблемных данных

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


ВЫБРАТЬ
    ИсходныеДанные.Ссылка КАК Ссылка,
    КОЛИЧЕСТВО(ИсходныеДанные.Ссылка) КАК КоличествоДублей
ИЗ
    (
        // Сюда вставляется полный текст запроса динамического списка без фигурных скобок СКД
        ВЫБРАТЬ
            СправочникНоменклатура.Ссылка КАК Ссылка
        ИЗ
            Справочник.Номенклатура КАК СправочникНоменклатура
            ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления._Доработка_УчетВесовойНоменклатуры.Остатки КАК Остатки
                ПО СправочникНоменклатура.Ссылка = Остатки.Номенклатура
    ) КАК ИсходныеДанные
СГРУППИРОВАТЬ ПО
    ИсходныеДанные.Ссылка
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(ИсходныеДанные.Ссылка) > 1

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

Способы устранения ошибки

В зависимости от архитектуры формы выберем один из следующих вариантов решения:

  1. Агрегация остатков во вложенных запросах: если в динамическом списке необходимо выводить общий остаток по всем складам или подразделениям, виртуальную таблицу остатков следует предварительно сгруппировать по номенклатуре, суммируя числовые показатели:
    
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            Остатки.Номенклатура КАК Номенклатура,
            СУММА(Остатки.ВесОстаток) КАК ВесОстаток,
            СУММА(Остатки.КоличествоОстаток) КАК КоличествоОстаток
        ИЗ
            РегистрНакопления._Доработка_УчетВесовойНоменклатуры.Остатки КАК Остатки
        СГРУППИРОВАТЬ ПО
            Остатки.Номенклатура
    ) КАК _Доработка_УчетВесовойНоменклатурыОстатки
        ПО СправочникНоменклатура.Ссылка = _Доработка_УчетВесовойНоменклатурыОстатки.Номенклатура
    
  2. Явное указание всех измерений в параметрах среза последних: при соединении с регистром цен обязательно передадим в первый или второй параметр виртуальной таблицы СрезПоследних все разделители (вид цены, характеристику, валюту), чтобы гарантировать возврат не более одной записи на одну номенклатуру.
  3. Очистка свойства «Основная таблица»: если характер отображаемых данных предполагает наличие нескольких строк на один элемент (например, отображение остатков в разрезе складов), очистим свойство Основная таблица в палитре свойств динамического списка. В этом случае платформа снимет жесткое ограничение на уникальность поля Ссылка, однако постраничная динамическая подгрузка больших массивов данных будет отключена.
← На главную