В процессе эксплуатации баз данных 1С на платформе MS SQL Server системные администраторы могут столкнуться с серьезной проблемой: повреждением индексов, которое не удается исправить стандартными средствами DBCC CHECKDB. Ситуация осложняется тем, что при попытке ручного пересоздания индексов или выполнения тестирования и исправления (ТиИ) система выдает ошибку 1505: «Выполнение инструкции CREATE UNIQUE INDEX прервано, поскольку обнаружен повторяющийся ключ». В данной статье мы подробно разберем, как выйти из этой ситуации, очистить таблицу от дублей и корректно вернуть индексы на место.
Обычно индексы в 1С обеспечивают уникальность записей. Однако при аварийном завершении работы SQL Server, сбоях дисковой подсистемы или некорректном выполнении команд восстановления целостности, физическая связь между индексом и данными может нарушиться. Если индекс был удален или поврежден, СУБД перестает контролировать уникальность, и в таблицу могут попасть идентичные строки. Рассмотрим основные типы индексов, которые чаще всего фигурируют в ошибках регистров накопления 1С:
LineNo).Если при попытке создания любого из этих индексов возникает ошибка 1505, это означает, что в таблице _AccumRg (регистр накопления) уже присутствуют дублирующиеся записи, которые физически не позволяют индексу стать уникальным.
Прежде всего, крайне важно создать полную резервную копию базы данных. Любые манипуляции с таблицами на уровне SQL могут привести к необратимой потере данных. Выясним причину ошибки, определив, какие именно записи являются дублями. Проанализируем ситуацию на примере индекса по регистратору и номеру строки (_ByRecorder_RN). Нам необходимо сгруппировать данные по полям, входящим в этот индекс.
Выполним следующий SQL-запрос для поиска проблемных строк в таблице вашего регистра (используйте инструменты, чтобы узнать структуру хранения базы данных и заменить _AccumRg7934 на имя вашей таблицы):
SELECT _RecorderTRef, _RecorderRRef, _LineNo, COUNT(*) as CountDuplicates
FROM [dbo].[_AccumRg7934]
GROUP BY _RecorderTRef, _RecorderRRef, _LineNo
HAVING COUNT(*) > 1;
Этот запрос покажет нам комбинации регистратора и номера строки, которые встречаются более одного раза. Именно эти записи блокируют создание индекса.
Разберем алгоритм очистки, предложенный опытными специалистами. Суть метода заключается в том, чтобы временно переместить дублирующиеся записи в отдельное место, удалить их из основной таблицы и вернуть только уникальные экземпляры.
DROP INDEX (для последующего обслуживания пригодится IndexOptimize). Сначала удаляйте некластерные индексы, а затем кластерный.Пример логики переноса данных для исправления:
-- 1. Создаем копию структуры во временную таблицу
SELECT TOP 0 * INTO _AccumRg7934_temp FROM _AccumRg7934;
-- 2. Переносим все записи, у которых есть дубли, во временную таблицу
INSERT INTO _AccumRg7934_temp
SELECT * FROM _AccumRg7934
WHERE _RecorderRRef IN (
SELECT _RecorderRRef FROM _AccumRg7934
GROUP BY _RecorderTRef, _RecorderRRef, _LineNo
HAVING COUNT(*) > 1
);
-- 3. Удаляем эти записи из основной таблицы
DELETE FROM _AccumRg7934
WHERE _RecorderRRef IN (SELECT _RecorderRRef FROM _AccumRg7934_temp);
Теперь, когда основная таблица не содержит дублей, мы можем восстановить индексы. Выполните скрипты CREATE UNIQUE CLUSTERED INDEX и CREATE UNIQUE INDEX, которые вы сохранили на предыдущем шаге. После того как индексы успешно созданы, нам нужно вернуть данные из временной таблицы _AccumRg7934_temp, но уже без дублей.
Рассмотрим, как это сделать:
-- Вставляем обратно только уникальные записи (по одной для каждого набора ключей)
INSERT INTO _AccumRg7934
SELECT DISTINCT * FROM _AccumRg7934_temp;
-- После успешного переноса временную таблицу можно удалить
DROP TABLE _AccumRg7934_temp;
Важный момент: использование SELECT DISTINCT * поможет, если записи идентичны полностью. Если же записи различаются в полях ресурсов (например, разное количество или сумма), вам пригодится универсальная работа с регистрами, чтобы вручную решить, какую из записей оставить, либо просуммировать их значения, чтобы не «потерять» остатки в регистре.
Если вы опасаетесь работать напрямую с SQL-запросами, можно попробовать решить проблему через Конфигуратор 1С, хотя этот метод не всегда срабатывает при наличии физических дублей в SQL. Посмотрим на альтернативные варианты:
ИндексДляИсправления) с типом Булево и установить у него свойство "Индексировать". При сохранении конфигурации 1С пересоздаст таблицу и попытается перестроить индексы. Если это не поможет из-за дублей, процедура прервется..dt данные упаковываются без индексов. При последующей загрузке в новую базу данных 1С создает таблицы и индексы с нуля. Если загрузка падает с ошибкой — значит, в данных точно есть дубли, которые нужно вычищать в SQL.Проанализируем ситуацию, когда данных в регистре очень много, а повреждения обширны. Если проблемный регистр — это регистр накопления (например, «Товары на складах»), данные в нем являются производными от документов. В крайнем случае можно прибегнуть к полной очистке таблицы:
TRUNCATE TABLE _AccumRg7934;
После этой команды таблица станет пустой, индексы создадутся мгновенно и без ошибок. Затем в 1С необходимо будет запустить процедуру «Проведение документов» за весь период или воспользоваться обработкой «Управление итогами» для пересчета итогов, если повреждена была таблица итогов (_AccumRgT), а не движений. Это гарантированно восстановит целостность базы, хотя и потребует значительного времени на выполнение.
Мы рассмотрели сложную техническую задачу по восстановлению структуры базы данных после сбоя. Помните, что появление дублей в уникальных индексах — это всегда следствие серьезного сбоя. Регулярная проверка базы данных командой DBCC CHECKDB и наличие актуальных бэкапов — лучшие средства защиты ваших нервов и данных пользователей.