При работе с SQL-версией 1С:Предприятие 7.7 администраторы и программисты часто сталкиваются с неприятной ситуацией: во время процедуры «Тестирование и исправление» (ТиС) на этапе «Пересчет служебных записей» система выдает ошибку о нарушении уникальности индекса в таблице _1SCRDOC. Попытки просто удалить графу отбора и добавить её заново обычно не помогают — при следующем пересчете 1С снова генерирует дубликаты. В этой статье мы подробно разберем, почему это происходит и как окончательно вылечить базу данных.
Для начала проанализируем структуру таблицы _1SCRDOC. Эта таблица отвечает за хранение информации о графах отбора и структуре подчиненности документов. В ней существует уникальный индекс, построенный по полям MDID (идентификатор графы отбора), PARENTVAL (идентификатор родительского объекта) и CHILDID (идентификатор подчиненного документа).
Рассмотрим типичный пример дублирующихся строк, который видит пользователь в SQL-запросе:
ROW_ID MDID PARENTVAL CHILD_DATE_TIME_IDDOC CHILDID FLAGS
2058429 48780 B1 IA 1CDZ 20190930EAEAY8 SL0T SL0T 1
2078352 48780 B1 IA 1CDZ 20190930EAGG40 SL0T SL0T 8
Обратим внимание на важные детали: MDID, PARENTVAL и CHILDID идентичны. Однако значения в полях CHILD_DATE_TIME_IDDOC и FLAGS различаются. Выясним, что означают эти поля:
_1SJOURN). Значение 8 говорит о том, что запись порождена бухгалтерской операцией или проводками документа (_1SENTRY).Суть проблемы: Процедура пересчета в 1С сканирует разные источники данных. Если время документа в журнале (_1SJOURN) не совпадает со временем в таблице проводок (_1SENTRY) или операций (_1SOPER) хотя бы на долю секунды, платформа считает их разными событиями и пытается вставить две разные записи в _1SCRDOC. Но так как ключевые поля (графа, родитель, документ) одинаковые, SQL Server блокирует вставку второй записи из-за нарушения уникальности индекса.
Самый простой, но «медленный» способ решения проблемы — это найти документы, у которых есть расхождения, и перепровести их. При перепроведении платформа автоматически синхронизирует время во всех связанных таблицах, записывая актуальное значение DATE_TIME_IDDOC из журнала в проводки.
Выясним, как найти такие документы. Мы можем использовать запрос к базе данных, чтобы сравнить время в журнале и время в таблице _1SCRDOC, где FLAGS = 8. Если они отличаются — этот документ является кандидатом на перепроведение. После этого необходимо повторно запустить ТиС для пересчета служебных данных.
Если документов много или период закрыт для редактирования, мы можем воспользоваться программным методом. Рассмотрим пример функции на языке 1С с использованием внешней компоненты 1С++, которая находит расхождения и исправляет поле CHILD_DATE_TIME_IDDOC в таблице _1SCRDOC, подтягивая верное значение из журнала документов.
Функция ВылечитьСтруктуру(прмДанные, прмЗапрос)
Перем Результат, ТекстЗапроса, Док;
Результат = Нет;
Если ПустоеЗначение(прмДанные.Документ) = 1 Тогда
Возврат Результат;
КонецЕсли;
Док = СоздатьОбъект("Документ");
Если Док.НайтиДокумент(прмДанные.Документ) = 1 Тогда
Если Док.Блокировка(1) = 1 Тогда
ТекстЗапроса = "
|UPDATE _1SCRDOC
| SET CHILD_DATE_TIME_IDDOC = Журнал.DATE_TIME_IDDOC
|FROM _1SCRDOC Отбор
|INNER JOIN _1SJOURN AS Журнал With (NOLOCK) ON (Журнал.IDDOC = Отбор.CHILDID) AND (Журнал.IDDOC = :Документ_подч)
|WHERE
| (Отбор.ROW_ID = :НомерСтроки)
| AND (Отбор.PARENTVAL = :Родитель)
| AND (Отбор.CHILD_DATE_TIME_IDDOC <> Журнал.DATE_TIME_IDDOC)
| AND (Отбор.CHILDID = :Документ_подч)
|";
прмЗапрос.УстановитьТекстовыйПараметр("НомерСтроки", прмДанные.ROW_ID);
прмЗапрос.УстановитьТекстовыйПараметр("Родитель", прмДанные.ЗначениеОтбора);
прмЗапрос.УстановитьТекстовыйПараметр("Документ_подч", прмДанные.Документ_подч);
Попытка
прмЗапрос.ВыполнитьИнструкцию(ТекстЗапроса);
Результат = Да;
Сообщить("Починили документ: " + Док.ТекущийДокумент());
Исключение
Сообщить("Ошибка при лечении структуры: " + Лев(ОписаниеОшибки(), 200));
КонецПопытки;
Док.Блокировка(0);
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции
Этот метод хорош тем, что он точечно исправляет записи в служебной таблице, не затрагивая основные данные учета. Однако, как мы выяснили ранее, корень проблемы лежит в расхождении _1SJOURN и _1SENTRY.
Наиболее эффективным и правильным с точки зрения целостности БД методом является синхронизация времени документа во всех таблицах-источниках. Мы должны гарантировать, что во всех таблицах, где есть поле DATE_TIME_IDDOC, оно совпадает со значением в главном журнале _1SJOURN.
Проанализируем шаги для выполнения этой операции:
_1SENTRY на основании _1SJOURN.
UPDATE _1SENTRY
SET DATE_TIME_IDDOC = J.DATE_TIME_IDDOC
FROM _1SENTRY E
INNER JOIN _1SJOURN J ON E.IDDOC = J.IDDOC
WHERE E.DATE_TIME_IDDOC <> J.DATE_TIME_IDDOC
_1SOPER.
UPDATE _1SOPER
SET DATE_TIME_IDDOC = J.DATE_TIME_IDDOC
FROM _1SOPER O
INNER JOIN _1SJOURN J ON O.IDDOC = J.IDDOC
WHERE O.DATE_TIME_IDDOC <> J.DATE_TIME_IDDOC
_1SCRDOC (с помощью команды TRUNCATE TABLE _1SCRDOC или через удаление конкретных строк) и запустить пересчет служебных данных в 1С. Поскольку время теперь везде одинаковое, дубликаты больше не возникнут.Перед выполнением любых прямых манипуляций с SQL-таблицами обязательно сделайте резервную копию базы данных. Работа напрямую с таблицами 1С 7.7 требует осторожности. Также убедитесь, что в момент выполнения запросов в базе нет активных пользователей, чтобы избежать блокировок и конфликтов доступа.
Подведем итог: Ошибка дублирования в _1SCRDOC — это лишь симптом «рассинхрона» времени документа между журналом и его проводками. Устранив причину через SQL-синхронизацию или перепроведение, вы избавитесь от ошибок при тестировании и исправлении базы.