«Обнаружено дублирование ключевых значений в колонках» в 1С: как найти и убрать дубли
Сообщение «Обнаружено дублирование ключевых значений в колонках» означает, что в данных есть две и более строки с одинаковой комбинацией значений в колонках, которые платформа считает ключом. Чаще всего это всплывает при записи набора записей регистра сведений (одинаковые измерения, а для периодического — измерения плюс период) и при загрузке результата запроса, который «размножило» соединение.
План действий: найти задвоенные комбинации запросом с
СГРУППИРОВАТЬ ПО и ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1,
затем либо свернуть таблицу методом Свернуть(), либо удалить лишние строки,
либо поправить сам запрос-источник.
Где возникает ошибка «Обнаружено дублирование ключевых значений в колонках»
Сама по себе таблица значений спокойно хранит любые повторы — ошибка возникает не при заполнении данных, а в тот момент, когда какой-то механизм платформы требует, чтобы комбинация значений в определённых колонках была уникальной. Типичные места:
-
Запись набора записей регистра сведений. В одном наборе оказались две
записи с одинаковыми значениями всех измерений (для периодического регистра — измерений
и периода). Часто это связка
Загрузить()из таблицы значений плюсЗаписать(). - Загрузка результата запроса. Соединение в запросе размножило строки (условие связи неполное, справа несколько совпадений), и в подготовленной таблице появились дубли по будущему ключу.
-
Свёртка не по тем колонкам.
Свернуть()выполняется без ошибок, но если в колонки группировки не вошли все поля будущего ключа, после свёртки строки всё равно останутся задвоенными с точки зрения регистра. - Служебные структуры по колонкам-ключам. Механизмы, которые строят соответствие «ключ → строка» — построение дерева, сводной таблицы, служебные индексы по набору колонок, — тоже рассчитывают на уникальность и сообщают о дублях, когда её нет.
Во всех случаях диагностика одна и та же: определить, какие колонки образуют ключ, и найти строки, где этот ключ повторяется.
Как найти дубли запросом: СГРУППИРОВАТЬ ПО и ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1
Универсальный приём: сгруппировать источник по колонкам ключа и оставить только группы, где строк больше одной. Если данные уже лежат в таблице значений, её можно передать в запрос параметром и поместить во временную таблицу:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Склад КАК Склад
|ПОМЕСТИТЬ ВТ_Источник
|ИЗ
| &Таблица КАК Т
|;
|ВЫБРАТЬ
| ВТ.Номенклатура КАК Номенклатура,
| ВТ.Склад КАК Склад,
| КОЛИЧЕСТВО(*) КАК КоличествоСтрок
|ИЗ
| ВТ_Источник КАК ВТ
|СГРУППИРОВАТЬ ПО
| ВТ.Номенклатура,
| ВТ.Склад
|ИМЕЮЩИЕ
| КОЛИЧЕСТВО(*) > 1";
Запрос.УстановитьПараметр("Таблица", ТаблицаЗагрузки);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Дубль: " + Выборка.Номенклатура + " / " + Выборка.Склад
+ " — строк: " + Выборка.КоличествоСтрок);
КонецЦикла;
В СГРУППИРОВАТЬ ПО перечисляйте все колонки ключа: для набора
записей регистра сведений это все измерения, а для периодического регистра — ещё и период.
Тот же запрос без секции ПОМЕСТИТЬ работает и по табличной части документа, и по
любой таблице базы — меняется только источник в ИЗ.
Как найти дубли в таблице значений кодом
Когда запрос избыточен (таблица небольшая или колонки содержат типы, которые неудобно гонять
через временные таблицы), дубли ищут прямо по таблице значений методом
НайтиСтроки():
// Ускоряем поиск: индекс по колонкам ключа
ТаблицаЗагрузки.Индексы.Добавить("Номенклатура, Склад");
Отбор = Новый Структура("Номенклатура, Склад");
Для Каждого Строка Из ТаблицаЗагрузки Цикл
ЗаполнитьЗначенияСвойств(Отбор, Строка);
Найденные = ТаблицаЗагрузки.НайтиСтроки(Отбор);
Если Найденные.Количество() > 1 Тогда
Сообщить("Дубль ключа: " + Строка.Номенклатура
+ " / " + Строка.Склад);
КонецЕсли;
КонецЦикла;
Без индекса каждый вызов НайтиСтроки() — перебор таблицы, и на больших объёмах
цикл станет квадратичным. Индекс, добавленный через Индексы.Добавить() по тому же
составу колонок, что и в структуре отбора, снимает проблему: платформа подбирает индекс по
точному соответствию состава колонок.
Удалить задвоенные строки, оставив по одной на ключ, можно тем же приёмом — обходом с конца:
Отбор = Новый Структура("Номенклатура, Склад");
Индекс = ТаблицаЗагрузки.Количество() - 1;
Пока Индекс >= 0 Цикл
Строка = ТаблицаЗагрузки[Индекс];
ЗаполнитьЗначенияСвойств(Отбор, Строка);
Найденные = ТаблицаЗагрузки.НайтиСтроки(Отбор);
// Первую строку ключа оставляем, остальные удаляем
Если Найденные.Количество() > 1 И Найденные[0] <> Строка Тогда
ТаблицаЗагрузки.Удалить(Строка);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Обход с конца обязателен: при удалении строк «вперёд» смещаются индексы ещё не обработанных строк.
Свёртка перед записью: метод Свернуть
Если дубли по ключу — это нормальные данные, которые нужно агрегировать (например, несколько строк прихода одной номенклатуры на один склад), правильное решение — свернуть таблицу до записи:
// Строки с одинаковыми Номенклатура + Склад
// склеятся в одну, Количество просуммируется
ТаблицаЗагрузки.Свернуть("Номенклатура, Склад", "Количество");
Два важных свойства метода: списки колонок не должны пересекаться, а колонки, не вошедшие ни в один из списков, после свёртки удаляются из таблицы. Поэтому в первый параметр включайте полный набор колонок ключа, а во второй — только те, что имеет смысл суммировать.
Свёртка — это именно суммирование. Для «последнего значения» (цены, курсы, статусы) она не подходит: сумма двух цен — бессмыслица. В таком случае лишние строки отбирают запросом на источнике (например, по максимальной дате записи) или чистят кодом, как в предыдущем разделе.
Дубли при записи набора записей регистра сведений
Самый частый сценарий ошибки: таблица подготовлена, грузим её в набор записей — и на
Записать() получаем дублирование ключевых значений. Набор записей одного
регистра не может содержать две записи с одинаковым ключом:
Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
// Ключ периодического регистра: Период + все измерения.
// Дубль в ТаблицаЦен по этой комбинации уронит запись.
Набор.Загрузить(ТаблицаЦен);
Набор.Записать(Истина);
Загрузить() заполняет записи набора по совпадению имён колонок таблицы и полей
записи, прежние записи набора при этом удаляются. Проверку уникальности проходит уже сама
запись набора, поэтому дубли нужно устранить в таблице до
Загрузить() — свёрткой, очисткой строк или исправлением запроса-источника.
Как исправить: отбор источника вместо чистки следствий
Удаление дублей постфактум — это лечение симптома. Надёжнее не создавать их:
-
Проверьте соединения в запросе-источнике. Если после
ЛЕВОЕ СОЕДИНЕНИЕстрок стало больше, чем в левой таблице, условие связи неполное — справа находится несколько совпадений. Добавьте недостающие поля в условие или предварительно сгруппируйте правую таблицу. -
Забирайте «последнюю» запись явно. Когда на один ключ приходится
несколько версий данных, выбирайте нужную запросом: группировка с
МАКСИМУМ()по дате плюс соединение по этой дате, а не первая попавшаяся строка. -
Группируйте на уровне запроса.
СГРУППИРОВАТЬ ПОвсем колонкам ключа с агрегатами по ресурсам даёт заведомо уникальные строки — свёртка в таблице значений становится не нужна. - Разбивайте запись по ключу отбора. Если данные грузятся порциями (по регистратору, по объекту), следите, чтобы одна и та же комбинация измерений не попадала в один набор дважды из разных порций источника.
Частые ошибки при разборе дублирования ключевых значений
- Забытый период у периодического регистра. Период входит в ключ. Если источник даёт время с точностью до секунд, а вы приводите его к началу дня, две записи одного дня по одинаковым измерениям превращаются в дубль.
-
NULL в колонке ключа. После левого соединения без
ЕСТЬNULL()в измерении остаётсяNULL; несколько таких строк — одинаковый ключ и та же ошибка. ЗаменяйтеNULLна пустое значение нужного типа ещё в запросе. -
Свёртка не по полному ключу.
Свернуть("Номенклатура", "Количество")при ключе «Номенклатура + Склад» удалит колонку Склад и испортит данные, а свёртка только по части измерений оставит дубли. Первый параметр — всегда полный список колонок ключа. - Пересекающиеся списки в Свернуть(). Одна и та же колонка в группировке и в суммировании — ошибка: списки колонок пересекаться не должны.
-
Поиск дублей НайтиСтроки() без индекса. На десятках тысяч строк цикл
с поиском без
Индексы.Добавить()работает минутами вместо секунд. - Чистка дублей вместо исправления соединения. Если строки размножает запрос, удаление лишних строк оставляет в данных случайную из версий — какая строка «выживет», не определено логикой задачи.
Частые вопросы об ошибке дублирования ключевых значений в 1С
Может ли метод Свернуть() сам вызвать эту ошибку?
Свёртка склеивает строки с одинаковыми значениями колонок группировки, поэтому дублей по этим колонкам после неё не остаётся. Ошибка проявляется дальше — при загрузке в регистр или построении служебных структур, — если свёртка выполнена не по полному набору колонок ключа. Отдельная ошибка самого метода — пересечение списков колонок в параметрах.
Входит ли период в ключ периодического регистра сведений?
Да. Ключ уникальности периодического регистра — период плюс все измерения. Две записи с одинаковыми измерениями, но разными периодами допустимы, а совпадение и периода, и измерений в одном наборе даёт дублирование ключевых значений.
Как быстрее всего найти дубли в большой таблице значений?
Передать таблицу параметром в запрос, поместить во временную таблицу и сгруппировать по колонкам ключа с условием ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1. Если нужен чистый код — добавить индекс по колонкам ключа и искать НайтиСтроки(): без индекса поиск на больших объёмах становится квадратичным.
ИИ Консоль кода 1С
Лайт
Для ознакомления с возможностями
- Полный набор инструментов без урезаний
- Без ввода карты
Старт
Для индивидуального использования
- До 5 конфигураций
- Встроенный агент запросов 1С
-
≈ 100 задач Подробнее про лимиты
- Техническая поддержка
Продвинутый
Популярный выборДля интенсивного использования или команд
- До 15 конфигураций
- Встроенный агент запросов 1С
-
≈ 300 задач Подробнее про лимиты
-
Свои подписки Codex, Claude и любые OpenAI-совместимые
-
Внешние обработки без Конфигуратора
- Приоритетная техническая поддержка