Разработчики, продолжающие поддерживать конфигурации на платформе 1С:Предприятие 7.7 в связке с СУБД MS SQL Server, регулярно сталкиваются со странным поведением стандартного механизма запросов. Типичная ситуация: несложный запрос по выборке остатков из регистра за длительный период (например, за один или несколько месяцев) отрабатывает практически мгновенно. Однако стоит пользователю или системе сформировать аналогичный запрос за короткий промежуток времени (всего за один день или даже за конкретную секунду), как сессия 1С намертво зависает, утилизируя ресурсы сервера. Снять такую задачу удается только аварийно.
В этой статье мы подробно разберем внутреннее устройство хранения итогов в 1С 7.7, выясним фундаментальные причины такого парадоксального поведения СУБД и рассмотрим все возможные пути решения проблемы — от простых локальных обходных приемов до профессионального перехода на прямые SQL-запросы.
Чтобы понять, почему возникает зависание, нам необходимо детально проанализировать физическую структуру хранения данных регистров в базе данных SQL Server под управлением 1С 7.7. Для каждого регистра в системе создаются две основные таблицы:
RA*): в эту таблицу записываются детальные строки каждого проведенного документа. Здесь содержатся измерения, ресурсы, реквизиты, направление движения (приход или расход), а также точное время и позиция документа на временной шкале.RG*): эта таблица хранит уже рассчитанные, агрегированные остатки. Важнейшая особенность 1С 7.7 заключается в том, что эти итоги рассчитываются и записываются строго на конец каждого календарного месяца (а также на Точку Актуальности — ТА).Рассмотрим, как ведет себя встроенный транслятор запросов («черный запрос») в зависимости от выбранного периода:
Ситуация А: Запрос за полный месяц. Когда мы указываем период запроса, совпадающий с границами месяца (например, с 1 по 30 число), оптимизатор 1С понимает, что на эти даты уже существуют предварительно рассчитанные данные в таблице итогов RG*. Ему нет необходимости анализировать миллионы строк первичных документов. Платформа просто выполняет быстрый селект из таблицы итогов, что занимает доли секунды.
Ситуация Б: Запрос за один день (или произвольную дату внутри месяца). Если нам требуются остатки на конкретный день (например, на 15-е число), готовой точки хранения итогов на эту дату в базе данных SQL просто нет. В этом случае платформа вынуждена запустить механизм так называемого «временного расчета». Для получения актуального остатка на 15-е число 1С 7.7 выполняет следующие действия:
RG*.RA* за период с начала месяца по 15-е число.Если в базе данных по данному регистру за первые две недели месяца были зафиксированы сотни тысяч или миллионы движений, СУБД начинает выполнять колоссальный объем работы по чтению и обработке строк. Ситуация усугубляется тем, что встроенный транслятор запросов 1С 7.7 генерирует SQL-код по стандартам конца 1990-х годов (под MS SQL Server 6.5 и 7.0), используя медленные серверные курсоры API_CURSOR, что приводит к полной блокировке потока и зависанию клиентского приложения.
Дополнительной проблемой встроенных («черных») запросов 1С 7.7 является их склонность к переносу вычислений на сторону клиента. Если в вашем запросе присутствуют сложные условия, накладываемые на справочники или списки значений (например, Условие (Товар в СписокЗначений)), транслятор часто не может преобразовать это условие в эффективное предложение WHERE IN (...) на языке T-SQL.
Вместо этого платформа выполняет гигантскую выборку сырых данных из SQL-сервера, перекачивает их по сети на компьютер пользователя (или на терминальный сервер), сохраняет во временные DBF-файлы в локальной папке Temp и производит окончательную фильтрацию уже силами локального процессора клиентской машины. При запросе за один день этот неэффективный алгоритм накладывается на тяжелый «временный расчет» остатков, полностью парализуя работу системы.
Если вам необходимо быстро устранить проблему зависания «здесь и сейчас» без глобальной переработки конфигурации, можно воспользоваться встроенным методом отключения SQL-оптимизации для конкретного объекта запроса. Для этого перед выполнением запроса вызывают специальный метод:
Запрос = СоздатьОбъект("Запрос");
Запрос.ВключитьSQL(0); // Отключаем генерацию T-SQL
Давайте проанализируем, как работает этот метод и в чем его особенности:
ВключитьSQL(0) заставляет платформу отказаться от попыток построить сложный и неоптимальный SQL-запрос для расчета итогов на сервере. Вместо этого 1С переходит в режим работы, аналогичный файловой версии (DBF). Данные из таблиц RG* и RA* извлекаются последовательно через ODBC-драйвер простыми порциями, а сам расчет остатков на нужную дату производится локально на стороне клиента.Еще один классический обходной путь, позволяющий разгрузить SQL Server без использования сторонних компонент, — это разделение логики получения данных на два независимых и максимально простых шага. Вместо того чтобы просить транслятор 1С рассчитать остатки и обороты «в одном флаконе» за проблемную дату, мы можем сделать следующее:
Шаг 1. Получаем остатки на начало нужного дня (или на конец предыдущего месяца). Для этого мы можем выгрузить готовые итоги из регистра напрямую в объект ТаблицаЗначений, минуя механизм запросов:
Рег = СоздатьОбъект("Регистр.ОстаткиТоваров");
// Выгружаем итоги на конец предыдущего месяца (это произойдет мгновенно)
Рег.ВременныйРасчет(1);
Рег.РассчитатьРегистрыНа(НачалоМесяца(НужнаяДата));
ТабОстатков = СоздатьОбъект("ТаблицаЗначений");
Рег.ВыгрузитьИтоги(ТабОстатков, 1, 1);
Шаг 2. Вторым простым запросом собираем только обороты за один конкретный интересующий нас день. Поскольку период запроса оборотов будет очень мал, а СУБД не придется вычислять остатки по движениям, этот запрос отработает моментально.
Шаг 3. Программно объединяем полученную таблицу начальных остатков и таблицу оборотов в одну результирующую таблицу значений на стороне клиента.
Такой подход требует написания дополнительного кода, но он работает в десятки раз быстрее стандартного «черного запроса» за один день.
Все вышеописанные методы являются лишь паллиативными мерами. Если вы хотите раз и навсегда избавить вашу систему на 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 проектировалась без учета современных многоядерных архитектур, её некорректные запросы могут парализовать работу всего сервера. Проанализируем ключевые настройки СУБД, которые помогут нивелировать эту проблему:
tempdb активно используется платформой 1С 7.7 для хранения промежуточных результатов расчетов. Обязательно перенесите файлы базы данных tempdb на самые быстрые физические диски (лучше всего на современные NVMe SSD) и разбейте файл данных tempdb на несколько равных частей (по количеству ядер процессора, но не более 8), чтобы избежать конфликтов при распределении страниц памяти (allocation bottlenecks).Резюмируя, можно сказать: зависание запроса за один день в 1С 7.7 — это классическая архитектурная проблема устаревшего транслятора запросов. Использование временного отключения SQL или разбиения на два этапа поможет вам оперативно стабилизировать работу системы. Однако для достижения максимальной производительности и полного исключения подобных зависаний в будущем мы настоятельно рекомендуем переписать критичные участки кода с использованием прямых запросов компоненты 1С++.