Как в динамическом списке 1С отобразить данные из связанной таблицы при дублировании записей?

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

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

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

Почему возникает ошибка дублирования ключей?

Проанализируем ситуацию. У нас есть основной справочник, например, «Номенклатура», и связанный с ним «НоменклатураПоставщиков». В последнем для одной нашей позиции «Котел ржавый» может быть несколько записей:

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

Рассмотрим, как решить эту задачу.

Решение 1: Удаление основной таблицы (быстрый способ с последствиями)

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

Как это работает? Когда основная таблица не указана, платформа перестает жестко контролировать уникальность ключа по полю «Ссылка» этой таблицы. Список становится, по сути, произвольным отчетом, и ошибка дублирования исчезает.

Преимущества:

Недостатки и риски:

Этот метод можно применять для простых отчетов или вспомогательных списков, где не требуется стандартная работа с элементами справочника. Для основных форм списков объектов этот подход не рекомендуется.

Решение 2: Вложенный запрос с группировкой (рекомендуемый способ)

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

В нашем случае задача — отобразить максимальный остаток у поставщиков. Давайте по шагам построим такой запрос.

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

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


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

Проанализируем этот запрос:

А что если все остатки нулевые?

В обсуждении возник логичный вопрос: какую позицию выберет функция МАКСИМУМ, если у одного поставщика остаток 0, и у другого тоже 0? Ответ прост: максимум из двух нулей — это ноль. Функция МАКСИМУМ() просто вернет числовое значение 0. Задача выбора конкретного поставщика в данном случае не стоит, так как нам нужно было получить только значение остатка. Если бы требовалось отобразить и наименование поставщика с максимальным остатком, задача бы усложнилась и потребовала бы более сложных конструкций запроса, например, с использованием временных таблиц или оконных функций.

Выводы

Ошибка «Обнаружено дублирование ключевых значений» — это не сбой платформы, а защитный механизм, обеспечивающий корректную работу динамических списков. Для ее решения:

  1. Избегайте быстрых, но рискованных методов, таких как отключение основной таблицы, если вам нужен полноценный функционал формы списка.
  2. Всегда используйте вложенные запросы с группировкой и агрегатными функциями (МАКСИМУМ, СУММА, КОЛИЧЕСТВО и т.д.) для подготовки данных перед соединением. Это гарантирует уникальность ключей и является надежным и масштабируемым решением.

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

← На главную