Как исправить ошибку «An expression services limit has been reached» при расчете стажа и открытии документов в 1С:ЗУП 3.1.13.75?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

Внезапное автоматическое или поспешное ручное обновление конфигурации 1С:Зарплата и управление персоналом (ЗУП) до релиза 3.1.13.75 способно полностью заблокировать работу кадровой службы. При попытке выполнить простейшие операции — открыть или провести документ Кадровый перевод, рассчитать больничный лист, открыть личную карточку сотрудника по форме Т-2 — система аварийно завершает работу, выдавая системную ошибку СУБД Microsoft SQL Server.

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

Анализ технической стороны проблемы и текста ошибки

При возникновении сбоя платформа выводит стандартное системное сообщение об ошибке следующего вида:

Microsoft SQL Server Native Client 11.0: Internal error: An expression services limit has been reached. Please look for potentially complex expressions in your query, and try to simplify them. (HRESULT=80040E14, SQLSrvr: SQLSTATE=42000, state=2, Severity=11, native=8632)

Анализируя стек вызовов, мы можем четко определить виновника происходящего. Ошибка стабильно воспроизводится в общем модуле кадрового учета:


{ОбщийМодуль.КадровыйУчет.Модуль(3007)}:    Запрос.Выполнить();
{ОбщийМодуль.КадровыйУчет.Модуль(2917)}:    СоздатьВТКадровыеДанныеСотрудников(ОписательВременныхТаблиц, ТолькоРазрешенные, КадровыеДанные, ПоляОтбораПериодическихДанных, ФормироватьСПериодичностьДень);
{ОбщийМодуль.КадровыйУчет.Модуль(90)}:    СоздатьНаДатуВТКадровыеДанныеСотрудников(Запрос.МенеджерВременныхТаблиц, ТолькоРазрешенные, СписокСотрудников, КадровыеДанные, ДатаПолученияДанных, ПоляОтбораПериодическихДанных, ФормироватьСПериодичностьДень);
{ОбщийМодуль.ЗарплатаКадрыРасширенный.Модуль(17097)}:        ДанныеСотрудника = КадровыйУчет.КадровыеДанныеСотрудников(Ложь, Сотрудник, "СеверныйСтажВид,СеверныйСтажЛет,СеверныйСтажМесяцев,СеверныйСтажДней", ДатаСобытия);
{Документ.КадровыйПеревод.Форма.ФормаДокумента.Форма(255)}:    ЗарплатаКадрыРасширенный.УстановитьЗначениеСевернойНадбавкиВФорме(ЭтаФорма, Объект.Сотрудник, Объект.ДатаНачала, Объект.ФизическоеЛицо);

Что же происходит под капотом системы? В релизе 3.1.13.75 разработчики существенно переработали механизмы динамического построения запросов для расчета стажей физических лиц (в частности, северного стажа). В процессе работы функция КадровыйУчет.КадровыеДанныеСотрудников генерирует огромный, перегруженный вложенными конструкциями SQL-запрос.

СУБД Microsoft SQL Server имеет жесткое внутреннее ограничение на глубину дерева выражений и количество вложенных элементов в запросе (лимит составляет 65 535 узлов). Когда динамически собранный запрос 1С превышает этот лимит (из-за обилия сложных логических условий CASE WHEN и многочисленных соединений таблиц), SQL Server отказывается его компилировать и возвращает ошибку native=8632.

Почему ошибка возникает только в клиент-серверном режиме?

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

  1. Ограничение конкретной СУБД: Ошибка Internal error: An expression services limit has been reached является специфическим ограничением СУБД MS SQL Server. Встроенный файловый движок 1С имеет иную архитектуру выполнения запросов и не содержит подобного лимита.
  2. Зависимость от объема данных: Запрос усложняется лавинообразно, если в базе присутствуют записи с некорректно заполненными периодами стажа, дубли по сотрудникам или пустые значения. В демо-базах такие искажения данных практически не встречаются.

Способ №1: Официальное исправление (Переход на исправленный релиз)

Фирма «1С» оперативно зарегистрировала данную проблему как критическую ошибку под номером 10219582. Ошибка официально исправлена в следующем релизе — 3.1.13.76.

Казалось бы, простейшее решение — обновиться до версии 3.1.13.76. Однако на практике спешить с этим обновлением не рекомендуется по следующим причинам:

Рекомендация по обновлению: Если вы приняли решение обновлять конфигурацию для устранения проблемы со стажами, рекомендуется пропустить версию 3.1.13.76 и сразу перейти на более поздние, стабильные накопительные релизы ветки 3.1.13 (например, 3.1.13.120 или выше) либо временно откатить конфигурацию на стабильный релиз 3.1.12.142.

Способ №2: Очистка некорректных данных в регистре сведений «Стажи физических лиц»

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

Опытным путем выяснено, что SQL-запрос ломается, когда сталкивается в регистре сведений СтажиФизическихЛиц с пустыми датами отсчета или техническими датами начала времен (например, 31.12.1899).

Проанализируем ситуацию и выполним пошаговое исправление данных:

Шаг 1. Поиск проблемных записей

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Стажи.ФизическоеЛицо КАК ФизическоеЛицо,
    |	Стажи.ВидСтажа КАК ВидСтажа,
    |	Стажи.ДатаОтсчета КАК ДатаОтсчета,
    |	Стажи.РазмерМесяцев КАК РазмерМесяцев,
    |	Стажи.РазмерДней КАК РазмерДней
    |ИЗ
    |	РегистрСведений.СтажиФизическихЛиц КАК Стажи
    |ГДЕ
    |	Стажи.ДатаОтсчета < ДАТАВРЕМЯ(1900, 1, 1)
    |	ИЛИ Стажи.ДатаОтсчета ЕСТЬ NULL";

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

Шаг 2. Обход блокировки SQL Server при интерактивном удалении

Если мы попытаемся открыть форму списка регистра сведений СтажиФизическихЛиц непосредственно в клиент-серверной базе через «Функции для технического специалиста» (или по навигационной ссылке e1cib/list/РегистрСведений.СтажиФизическихЛиц), платформа, скорее всего, снова выбросит ошибку компиляции SQL-запроса, не дав нам даже увидеть список записей.

Чтобы обойти эту блокировку, выполним следующие действия:

  1. Создадим резервную копию рабочей базы данных.
  2. Развернем эту копию локально в файловом режиме работы.
  3. В файловой базе откроем регистр сведений Стажи физических лиц. Поскольку файловый движок не падает на сложных запросах, мы без проблем сможем отфильтровать записи по дате отсчета (меньше 1900 года).
  4. Удалим некорректные пустые строки либо исправим даты отсчета на реальные (например, на дату приема сотрудника на работу).

Шаг 3. Программное исправление записей стажа

Для автоматизации процесса очистки в рабочей базе (без ее перевода в файловый режим) мы можем написать программный обработчик, который работает непосредственно через объектную модель наборов записей, не вызывая тяжелых запросов динамических списков платформы:


// Программный скрипт для очистки поврежденных записей стажей
ПроблемныеФизическиеЛица = Новый Массив;

// Заполним массив ссылками на физлиц, найденных на Шаге 1
// (Вставьте сюда конкретные ссылки или выберите их запросом)

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

После удаления битых записей и очистки регистра от пустых дат СУБД MS SQL сможет успешно компилировать входящие запросы платформы. Документы кадрового перевода начнут открываться в штатном режиме.

Важные выводы по администрированию обновлений 1С:ЗУП

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

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

Для минимизации подобных рисков рекомендуется следовать четкому регламенту:

  1. Отключите автоматическое получение и установку обновлений в настройках интернет-поддержки рабочей базы ЗУП.
  2. Всегда разворачивайте тестовую копию рабочей базы в аналогичном (клиент-серверном) режиме работы перед установкой любого нового релиза.
  3. Проводите ручное выборочное тестирование ключевых бизнес-процессов на тестовой базе: проведение приемов, переводов, увольнений, начисление зарплаты и формирование регламентированных отчетов.
  4. Выдерживайте паузу в 3–5 дней с момента выхода нового релиза перед его установкой на рабочую базу, регулярно отслеживая сообщения на профессиональных форумах и официальном портале технической поддержки «1С» на предмет обнаружения критических багов.
← На главную