Как исправить ошибку «В этой транзакции уже происходили ошибки» при обновлении информационной базы на SQL?

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При создании новой информационной базы из шаблона или выполнении процедур обновления в клиент-серверном варианте (MS SQL Server / PostgreSQL) разработчики и администраторы нередко сталкиваются с исключением вида: «Ошибка при вызове метода контекста (Следующий): В этой транзакции уже происходили ошибки». Разберем механизм возникновения этой ошибки, методы её локализации и способы устранения.

Рассмотрим типовую ситуацию, когда ошибка возникает в цикле обхода выборки счетов или справочников с одновременной записью объектов:


Процедура УстановитьПараметрыСчетовБУиНУ()
    НачатьТранзакцию();
    СостояниеОбновления("Установка запрета использования в проводках групп счетов...");
    
    ВыборкаСчетов = ПланыСчетов.Хозрасчетный.ВыбратьИерархически();
    Пока ВыборкаСчетов.Следующий() Цикл
        Если НЕ ЗначениеЗаполнено(ВыборкаСчетов.Ссылка.Родитель) Тогда 
            Продолжить; 
        КонецЕсли;
        
        Объект = ВыборкаСчетов.Ссылка.Родитель.ПолучитьОбъект();
        Если Объект.ЗапретитьИспользоватьВПроводках Тогда 
            Продолжить; 
        КонецЕсли;
        
        Объект.ЗапретитьИспользоватьВПроводках = Истина;
        Объект.Записать();
    КонецЦикла;
    
    ЗафиксироватьТранзакцию();
КонецПроцедуры

Почему возникает ошибка «В этой транзакции уже происходили ошибки»?

Проанализируем природу этого сообщения платформы. Сообщение «В этой транзакции уже происходили ошибки» никогда не является первопричиной сбоя — это лишь следствие того, что ранее внутри открытой транзакции уже произошло исключение, переведшее транзакцию СУБД в статус прерванной (doomed/aborted). После этого любое обращение к базе данных (включая метод Следующий() выборки) вызывает ошибку контекста.

Выделим основные причины, почему это происходит именно на SQL-сервере:

  1. Конфликт серверного курсора и модификации данных: Метод ВыбратьИерархически() или Выбрать() открывает курсор чтения на сервере СУБД. Выполнение операции Объект.Записать() в том же потоке внутри незафиксированной транзакции блокирует и изменяет таблицу, что может приводить к инвалидации курсора выборки. В файловом варианте курсоры работают в оперативной памяти иначе, поэтому ошибка там не проявляется.
  2. Скрытые исключения в событиях объектов: При вызове метода Объект.Записать() срабатывают обработчики ПередЗаписью, ПриЗаписи, подписки на события или контроль итогов. Если в них возникает ошибка, перехваченная пустой конструкцией Попытка ... Исключение без вызова ВызватьИсключение, платформа 1С подавляет вывод текста ошибки, но на уровне СУБД транзакция уже помечена как ошибочная.
  3. Некорректные значения дат (смещение 0 и 2000): Если в таблицах шаблона присутствуют пустые или некорректные даты (например, 01.01.0001), MS SQL Server при смещении дат 0 завершает запись ошибкой переполнения типа DATETIME (минимальная поддерживаемая дата в MS SQL — 01.01.1753).

Способ 1. Разделение выборки данных и их модификации

Посмотрим, как правильно переписать проблемный алгоритм, чтобы исключить конфликт чтения по открытому курсору и записи объектов. Заменим обход выборки на предварительное чтение ссылок в массив или таблицу значений с помощью запроса:


Процедура УстановитьПараметрыСчетовБУиНУ()
    // 1. Сначала считываем все нужные ссылки без удержания курсора
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   Хозрасчетный.Родитель КАК СсылкаРодитель
        |ИЗ
        |   ПланСчетов.Хозрасчетный КАК Хозрасчетный
        |ГДЕ
        |   Хозрасчетный.Родитель <> ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ПустаяСсылка)
        |   И НЕ Хозрасчетный.Родитель.ЗапретитьИспользоватьВПроводках";
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    // 2. Модифицируем объекты отдельными порциями или поштучно
    Пока Выборка.Следующий() Цикл
        НачатьТранзакцию();
        Попытка
            Объект = Выборка.СсылкаРодитель.ПолучитьОбъект();
            Если Объект <> Неопределено И НЕ Объект.ЗапретитьИспользоватьВПроводках Тогда
                Объект.ЗапретитьИспользоватьВПроводках = Истина;
                Объект.Записать();
            КонецЕсли;
            ЗафиксироватьТранзакцию();
        Исключение
            ОтменитьТранзакцию();
            ВызватьИсключение;
        КонецПопытки;
    КонецЦикла;
КонецПроцедуры

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

Способ 2. Поиск первичной ошибки через отладчик и Технологический журнал

Если код типовой конфигурации менять нежелательно, необходимо найти, какая именно операция вызывает первоначальный сбой СУБД. Разберем шаги диагностики:

  1. Настройка остановки по ошибке в конфигураторе: В меню Отладка -> Остановка по ошибке включите флаг «Останавливаться при вызове исключения». Это заставит конфигуратор остановиться в месте реальной ошибки (например, в обработчике подписки на события ПриЗаписи), даже если она обернута в блок Попытка ... Исключение.
  2. Анализ технологического журнала: Настройте сбор событий EXCP, EXCPCNTX и DBMSSQL в файле logcfg.xml. В логе найдите первое событие исключения до возникновения ошибки Следующий() — там будет содержаться оригинальное сообщение от СУБД (например, нарушение уникальности индекса или выход за границы диапазона дат).

Способ 3. Инициализация через файловый режим (обходной путь)

Если база создается из шаблона и сбой происходит исключительно на этапе первоначального автозаполнения регистров и планов счетов:

  1. Создайте новую информационную базу в файловом варианте.
  2. Запустите режим «1С:Предприятие» и дождитесь полного завершения процедур обновления и первоначального заполнения данных.
  3. В конфигураторе выполните выгрузку информационной базы в файл .dt (меню Администрирование -> Выгрузить информационную базу...).
  4. Создайте чистую базу на сервере 1С:Предприятия с СУБД MS SQL / PostgreSQL и загрузите полученный файл выгрузки через Администрирование -> Загрузить информационную базу....
← На главную