Почему стандартный запрос остатков за один день зависает в 1С 7.7 на SQL Server, а за месяц работает быстро?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

Разработчики, продолжающие поддерживать конфигурации на платформе 1С:Предприятие 7.7 в связке с СУБД MS SQL Server, регулярно сталкиваются со странным поведением стандартного механизма запросов. Типичная ситуация: несложный запрос по выборке остатков из регистра за длительный период (например, за один или несколько месяцев) отрабатывает практически мгновенно. Однако стоит пользователю или системе сформировать аналогичный запрос за короткий промежуток времени (всего за один день или даже за конкретную секунду), как сессия 1С намертво зависает, утилизируя ресурсы сервера. Снять такую задачу удается только аварийно.

В этой статье мы подробно разберем внутреннее устройство хранения итогов в 1С 7.7, выясним фундаментальные причины такого парадоксального поведения СУБД и рассмотрим все возможные пути решения проблемы — от простых локальных обходных приемов до профессионального перехода на прямые SQL-запросы.

Архитектурная причина: как устроены таблицы итогов и движений в 1С 7.7

Чтобы понять, почему возникает зависание, нам необходимо детально проанализировать физическую структуру хранения данных регистров в базе данных SQL Server под управлением 1С 7.7. Для каждого регистра в системе создаются две основные таблицы:

  1. Таблица движений (имя таблицы в SQL начинается с RA*): в эту таблицу записываются детальные строки каждого проведенного документа. Здесь содержатся измерения, ресурсы, реквизиты, направление движения (приход или расход), а также точное время и позиция документа на временной шкале.
  2. Таблица итогов (имя таблицы в SQL начинается с RG*): эта таблица хранит уже рассчитанные, агрегированные остатки. Важнейшая особенность 1С 7.7 заключается в том, что эти итоги рассчитываются и записываются строго на конец каждого календарного месяца (а также на Точку Актуальности — ТА).

Рассмотрим, как ведет себя встроенный транслятор запросов («черный запрос») в зависимости от выбранного периода:

Ситуация А: Запрос за полный месяц. Когда мы указываем период запроса, совпадающий с границами месяца (например, с 1 по 30 число), оптимизатор 1С понимает, что на эти даты уже существуют предварительно рассчитанные данные в таблице итогов RG*. Ему нет необходимости анализировать миллионы строк первичных документов. Платформа просто выполняет быстрый селект из таблицы итогов, что занимает доли секунды.

Ситуация Б: Запрос за один день (или произвольную дату внутри месяца). Если нам требуются остатки на конкретный день (например, на 15-е число), готовой точки хранения итогов на эту дату в базе данных SQL просто нет. В этом случае платформа вынуждена запустить механизм так называемого «временного расчета». Для получения актуального остатка на 15-е число 1С 7.7 выполняет следующие действия:

Если в базе данных по данному регистру за первые две недели месяца были зафиксированы сотни тысяч или миллионы движений, СУБД начинает выполнять колоссальный объем работы по чтению и обработке строк. Ситуация усугубляется тем, что встроенный транслятор запросов 1С 7.7 генерирует SQL-код по стандартам конца 1990-х годов (под MS SQL Server 6.5 и 7.0), используя медленные серверные курсоры API_CURSOR, что приводит к полной блокировке потока и зависанию клиентского приложения.

Проблема транслятора «черных запросов»

Дополнительной проблемой встроенных («черных») запросов 1С 7.7 является их склонность к переносу вычислений на сторону клиента. Если в вашем запросе присутствуют сложные условия, накладываемые на справочники или списки значений (например, Условие (Товар в СписокЗначений)), транслятор часто не может преобразовать это условие в эффективное предложение WHERE IN (...) на языке T-SQL.

Вместо этого платформа выполняет гигантскую выборку сырых данных из SQL-сервера, перекачивает их по сети на компьютер пользователя (или на терминальный сервер), сохраняет во временные DBF-файлы в локальной папке Temp и производит окончательную фильтрацию уже силами локального процессора клиентской машины. При запросе за один день этот неэффективный алгоритм накладывается на тяжелый «временный расчет» остатков, полностью парализуя работу системы.

Вариант решения №1: Принудительное отключение SQL в запросе

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


Запрос = СоздатьОбъект("Запрос");
Запрос.ВключитьSQL(0); // Отключаем генерацию T-SQL

Давайте проанализируем, как работает этот метод и в чем его особенности:

Вариант решения №2: Разбиение на два простых запроса

Еще один классический обходной путь, позволяющий разгрузить SQL Server без использования сторонних компонент, — это разделение логики получения данных на два независимых и максимально простых шага. Вместо того чтобы просить транслятор 1С рассчитать остатки и обороты «в одном флаконе» за проблемную дату, мы можем сделать следующее:

Шаг 1. Получаем остатки на начало нужного дня (или на конец предыдущего месяца). Для этого мы можем выгрузить готовые итоги из регистра напрямую в объект ТаблицаЗначений, минуя механизм запросов:


Рег = СоздатьОбъект("Регистр.ОстаткиТоваров");
// Выгружаем итоги на конец предыдущего месяца (это произойдет мгновенно)
Рег.ВременныйРасчет(1);
Рег.РассчитатьРегистрыНа(НачалоМесяца(НужнаяДата));
ТабОстатков = СоздатьОбъект("ТаблицаЗначений");
Рег.ВыгрузитьИтоги(ТабОстатков, 1, 1);

Шаг 2. Вторым простым запросом собираем только обороты за один конкретный интересующий нас день. Поскольку период запроса оборотов будет очень мал, а СУБД не придется вычислять остатки по движениям, этот запрос отработает моментально.

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

Такой подход требует написания дополнительного кода, но он работает в десятки раз быстрее стандартного «черного запроса» за один день.

Вариант решения №3: Кардинальный переход на прямые SQL-запросы через 1С++

Все вышеописанные методы являются лишь паллиативными мерами. Если вы хотите раз и навсегда избавить вашу систему на 1С 7.7 от проблем с производительностью при работе на SQL Server, единственным верным решением является отказ от стандартных «черных запросов» в пользу использования внешней компоненты 1С++ и технологии прямых запросов (Direct SQL).

Компонента 1С++ предоставляет разработчику класс ODBCRecordset, который позволяет писать запросы на чистом, полноценном языке T-SQL, минуя устаревший транслятор платформы. Оптимизатор MS SQL Server получает чистый SQL-код, строит идеальный план выполнения и отрабатывает запрос за миллисекунды.

Для решения проблемы расчета остатков на произвольную дату в 1С++ реализован уникальный механизм — виртуальные таблицы. Специальный метапарсер 1С++ на лету заменяет конструкции вида $РегистрОстаткиОбороты на сложнейший, но максимально оптимизированный SQL-код, который соединяет таблицы RG* и RA* наиболее эффективным для СУБД способом.

Рассмотрим пример того, как выглядит прямой запрос для получения остатков на конкретную дату с использованием 1С++:


// Создаем объект для работы с прямым запросом
ЗапросSQL = СоздатьОбъект("ODBCRecordset");

// Формируем текст запроса на языке T-SQL с метасимволикой 1С++
ТекстЗапроса = "
|SELECT
|    Запрос.Товар [Товар $Справочник.Номенклатура],
|    Запрос.ОстатокТовараОстаток AS Остаток
|FROM
|    $РегистрОстатки.ОстаткиТоваров(:ВыбДата,,
|                                   Товар,
|                                   (ОстатокТовара),) AS Запрос
|";

// Устанавливаем параметры запроса
ЗапросSQL.УстановитьТекстовыйПараметр("ВыбДата", НужнаяДата);

// Выполняем запрос напрямую в SQL Server и получаем результат в Таблицу Значений
ТабРезультат = ЗапросSQL.ВыполнитьИнструкцию(ТекстЗапроса);

Этот запрос за произвольный один день (или на любую секунду внутри месяца) выполнится на сервере за сотые доли секунды. Вся тяжелая математика расчета остатков перекладывается на плечи SQL Server, который выполняет её на уровне индексов в оперативной памяти, не загружая клиентскую сеть и жесткие диски временными файлами.

Рекомендации по настройке MS SQL Server для работы с 1С 7.7

Помимо оптимизации самих запросов, крайне важно правильно сконфигурировать сервер СУБД MS SQL Server. Поскольку платформа 1С 7.7 проектировалась без учета современных многоядерных архитектур, её некорректные запросы могут парализовать работу всего сервера. Проанализируем ключевые настройки СУБД, которые помогут нивелировать эту проблему:

  1. Ограничение параллелизма (настройка MAXDOP): По умолчанию SQL Server пытается распараллелить выполнение одного сложного запроса на все доступные ядра процессора. Когда 1С 7.7 отправляет тяжелый «черный запрос» за один день, СУБД выделяет под него все процессорные ресурсы. В итоге зависает не только один пользователь, но и вся база данных для остальных сотрудников. Чтобы предотвратить это, обязательно установите параметр Max Degree of Parallelism (MAXDOP) в значение 1 в свойствах вашего SQL-сервера. Это заставит СУБД выполнять каждый отдельный запрос строго на одном ядре процессора.
  2. Регулярное обновление статистики: Оптимизатор SQL Server строит план выполнения запроса на основе статистических данных о распределении значений в таблицах. Если статистика устарела, сервер может выбрать неоптимальный индекс или решить сканировать всю таблицу целиком (Table Scan). Настройте регламентное задание на ежедневное (или даже ежечасное в периоды высокой активности) обновление статистики для всех таблиц базы данных 1С.
  3. Оптимизация TempDB: База данных временных таблиц tempdb активно используется платформой 1С 7.7 для хранения промежуточных результатов расчетов. Обязательно перенесите файлы базы данных tempdb на самые быстрые физические диски (лучше всего на современные NVMe SSD) и разбейте файл данных tempdb на несколько равных частей (по количеству ядер процессора, но не более 8), чтобы избежать конфликтов при распределении страниц памяти (allocation bottlenecks).

Резюмируя, можно сказать: зависание запроса за один день в 1С 7.7 — это классическая архитектурная проблема устаревшего транслятора запросов. Использование временного отключения SQL или разбиения на два этапа поможет вам оперативно стабилизировать работу системы. Однако для достижения максимальной производительности и полного исключения подобных зависаний в будущем мы настоятельно рекомендуем переписать критичные участки кода с использованием прямых запросов компоненты 1С++.

← На главную