Ошибки платформы 1С

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

Коротко

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

План действий: найти задвоенные комбинации запросом с СГРУППИРОВАТЬ ПО и ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1, затем либо свернуть таблицу методом Свернуть(), либо удалить лишние строки, либо поправить сам запрос-источник.

Где возникает ошибка «Обнаружено дублирование ключевых значений в колонках»

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

  • Запись набора записей регистра сведений. В одном наборе оказались две записи с одинаковыми значениями всех измерений (для периодического регистра — измерений и периода). Часто это связка Загрузить() из таблицы значений плюс Записать().
  • Загрузка результата запроса. Соединение в запросе размножило строки (условие связи неполное, справа несколько совпадений), и в подготовленной таблице появились дубли по будущему ключу.
  • Свёртка не по тем колонкам. Свернуть() выполняется без ошибок, но если в колонки группировки не вошли все поля будущего ключа, после свёртки строки всё равно останутся задвоенными с точки зрения регистра.
  • Служебные структуры по колонкам-ключам. Механизмы, которые строят соответствие «ключ → строка» — построение дерева, сводной таблицы, служебные индексы по набору колонок, — тоже рассчитывают на уникальность и сообщают о дублях, когда её нет.

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

Как найти дубли запросом: СГРУППИРОВАТЬ ПО и ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1

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

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |   Т.Номенклатура КАК Номенклатура,
    |   Т.Склад КАК Склад
    |ПОМЕСТИТЬ ВТ_Источник
    |ИЗ
    |   &Таблица КАК Т
    |;
    |ВЫБРАТЬ
    |   ВТ.Номенклатура КАК Номенклатура,
    |   ВТ.Склад КАК Склад,
    |   КОЛИЧЕСТВО(*) КАК КоличествоСтрок
    |ИЗ
    |   ВТ_Источник КАК ВТ
    |СГРУППИРОВАТЬ ПО
    |   ВТ.Номенклатура,
    |   ВТ.Склад
    |ИМЕЮЩИЕ
    |   КОЛИЧЕСТВО(*) > 1";
Запрос.УстановитьПараметр("Таблица", ТаблицаЗагрузки);

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить("Дубль: " + Выборка.Номенклатура + " / " + Выборка.Склад
        + " — строк: " + Выборка.КоличествоСтрок);
КонецЦикла;

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

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

Как найти дубли в таблице значений кодом

Когда запрос избыточен (таблица небольшая или колонки содержат типы, которые неудобно гонять через временные таблицы), дубли ищут прямо по таблице значений методом НайтиСтроки():

// Ускоряем поиск: индекс по колонкам ключа
ТаблицаЗагрузки.Индексы.Добавить("Номенклатура, Склад");

Отбор = Новый Структура("Номенклатура, Склад");
Для Каждого Строка Из ТаблицаЗагрузки Цикл
    ЗаполнитьЗначенияСвойств(Отбор, Строка);
    Найденные = ТаблицаЗагрузки.НайтиСтроки(Отбор);
    Если Найденные.Количество() > 1 Тогда
        Сообщить("Дубль ключа: " + Строка.Номенклатура
            + " / " + Строка.Склад);
    КонецЕсли;
КонецЦикла;
НайтиСтроки
ПараметрыОтбораструктура: имя колонки — искомое значение
Массив

Без индекса каждый вызов НайтиСтроки() — перебор таблицы, и на больших объёмах цикл станет квадратичным. Индекс, добавленный через Индексы.Добавить() по тому же составу колонок, что и в структуре отбора, снимает проблему: платформа подбирает индекс по точному соответствию состава колонок.

Удалить задвоенные строки, оставив по одной на ключ, можно тем же приёмом — обходом с конца:

Отбор = Новый Структура("Номенклатура, Склад");
Индекс = ТаблицаЗагрузки.Количество() - 1;
Пока Индекс >= 0 Цикл
    Строка = ТаблицаЗагрузки[Индекс];
    ЗаполнитьЗначенияСвойств(Отбор, Строка);
    Найденные = ТаблицаЗагрузки.НайтиСтроки(Отбор);
    // Первую строку ключа оставляем, остальные удаляем
    Если Найденные.Количество() > 1 И Найденные[0] <> Строка Тогда
        ТаблицаЗагрузки.Удалить(Строка);
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

Обход с конца обязателен: при удалении строк «вперёд» смещаются индексы ещё не обработанных строк.

Свёртка перед записью: метод Свернуть

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

// Строки с одинаковыми Номенклатура + Склад
// склеятся в одну, Количество просуммируется
ТаблицаЗагрузки.Свернуть("Номенклатура, Склад", "Количество");
Свернуть
КолонкиГруппировокколонки, по совпадению которых строки сворачиваются в одну
КолонкиСуммированияколонки, значения которых накапливаются

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

Свёртка — это именно суммирование. Для «последнего значения» (цены, курсы, статусы) она не подходит: сумма двух цен — бессмыслица. В таком случае лишние строки отбирают запросом на источнике (например, по максимальной дате записи) или чистят кодом, как в предыдущем разделе.

Дубли при записи набора записей регистра сведений

Самый частый сценарий ошибки: таблица подготовлена, грузим её в набор записей — и на Записать() получаем дублирование ключевых значений. Набор записей одного регистра не может содержать две записи с одинаковым ключом:

Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();

// Ключ периодического регистра: Период + все измерения.
// Дубль в ТаблицаЦен по этой комбинации уронит запись.
Набор.Загрузить(ТаблицаЦен);
Набор.Записать(Истина);
Загрузить
ТаблицаЗначенийисточник; колонки сопоставляются с полями записи по именам

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

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

Как исправить: отбор источника вместо чистки следствий

Удаление дублей постфактум — это лечение симптома. Надёжнее не создавать их:

  • Проверьте соединения в запросе-источнике. Если после ЛЕВОЕ СОЕДИНЕНИЕ строк стало больше, чем в левой таблице, условие связи неполное — справа находится несколько совпадений. Добавьте недостающие поля в условие или предварительно сгруппируйте правую таблицу.
  • Забирайте «последнюю» запись явно. Когда на один ключ приходится несколько версий данных, выбирайте нужную запросом: группировка с МАКСИМУМ() по дате плюс соединение по этой дате, а не первая попавшаяся строка.
  • Группируйте на уровне запроса. СГРУППИРОВАТЬ ПО всем колонкам ключа с агрегатами по ресурсам даёт заведомо уникальные строки — свёртка в таблице значений становится не нужна.
  • Разбивайте запись по ключу отбора. Если данные грузятся порциями (по регистратору, по объекту), следите, чтобы одна и та же комбинация измерений не попадала в один набор дважды из разных порций источника.

Частые ошибки при разборе дублирования ключевых значений

  • Забытый период у периодического регистра. Период входит в ключ. Если источник даёт время с точностью до секунд, а вы приводите его к началу дня, две записи одного дня по одинаковым измерениям превращаются в дубль.
  • NULL в колонке ключа. После левого соединения без ЕСТЬNULL() в измерении остаётся NULL; несколько таких строк — одинаковый ключ и та же ошибка. Заменяйте NULL на пустое значение нужного типа ещё в запросе.
  • Свёртка не по полному ключу. Свернуть("Номенклатура", "Количество") при ключе «Номенклатура + Склад» удалит колонку Склад и испортит данные, а свёртка только по части измерений оставит дубли. Первый параметр — всегда полный список колонок ключа.
  • Пересекающиеся списки в Свернуть(). Одна и та же колонка в группировке и в суммировании — ошибка: списки колонок пересекаться не должны.
  • Поиск дублей НайтиСтроки() без индекса. На десятках тысяч строк цикл с поиском без Индексы.Добавить() работает минутами вместо секунд.
  • Чистка дублей вместо исправления соединения. Если строки размножает запрос, удаление лишних строк оставляет в данных случайную из версий — какая строка «выживет», не определено логикой задачи.

Частые вопросы об ошибке дублирования ключевых значений в 1С

Может ли метод Свернуть() сам вызвать эту ошибку?

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

Входит ли период в ключ периодического регистра сведений?

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

Как быстрее всего найти дубли в большой таблице значений?

Передать таблицу параметром в запрос, поместить во временную таблицу и сгруппировать по колонкам ключа с условием ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1. Если нужен чистый код — добавить индекс по колонкам ключа и искать НайтиСтроки(): без индекса поиск на больших объёмах становится квадратичным.

Тарифы

ИИ Консоль кода 1С

Лайт

Бесплатно
≈ 1,5 млн токенов

Для ознакомления с возможностями

  • Все функции бесплатно на 2 дня +2 дня за подписку на Telegram или Max
  • Полный набор инструментов без урезаний
  • Без ввода карты
Начать бесплатно

Старт

1990 руб. в месяц
≈ 10 млн токенов

Для индивидуального использования

Выбрать тариф

Продвинутый

Популярный выбор
3990 руб. в месяц
≈ 25 млн токенов

Для интенсивного использования или команд

  • До 15 конфигураций
  • Встроенный агент запросов 1С
  • Свои подписки Codex, Claude и любые OpenAI-совместимые
  • Внешние обработки без Конфигуратора
  • Приоритетная техническая поддержка
Выбрать тариф