Как ускорить получение остатков в 1С 7.7 SQL через прямые запросы с фильтрацией по реквизитам документов?

Программист 1С v7.7 Управленческий учет
← На главную

Перевод базы данных 1С 7.7 с формата DBF на SQL часто преподносит неприятный сюрприз: стандартные ("черные") запросы, которые раньше работали быстро, начинают ощутимо тормозить. Это происходит из-за особенностей работы SQL-сервера с итогами и неоптимального построения планов запросов движком 1С. Особенно остро проблема встает, когда необходимо получить остатки по регистру, накладывая фильтр на реквизит, который не является измерением регистра, а принадлежит документу (например, "Фирма" или "Раздельный учет").

В этой статье мы разберем, как уйти от медленных стандартных запросов и реализовать высокопроизводительный прямой SQL-запрос с использованием библиотеки 1С++. Мы проанализируем структуру таблиц и научимся правильно связывать остатки регистров с журналом документов.

Постановка задачи и анализ проблемы

Рассмотрим ситуацию: в базе есть регистр ОстаткиТоваров. Одно из его измерений — Документ (тип "Документ неопределенного вида"), который выполняет роль партии. Нам нужно получить остатки только по тем фирмам, у которых установлен определенный признак (например, РаздельныйУчет = 1). Проблема в том, что "Фирма" — это общий реквизит документов, а не измерение регистра.

Стандартный запрос 1С 7.7 в этом случае выполняет соединение таблиц крайне неэффективно. Наша задача — написать прямой запрос, который сделает это на уровне SQL-сервера максимально быстро.

Шаг 1: Использование виртуальных таблиц 1С++

Для работы с остатками в 1С++ предусмотрен макрос $РегистрОстатки. Он автоматически преобразуется в сложный SQL-запрос, который учитывает актуальные итоги и движения за текущий период. Начнем с базовой структуры получения остатков по конкретному складу и списку товаров.


рс = СоздатьОбъект("ODBCRecordset");
ТекстЗапроса = "
|SELECT Остатки.Товар [Товар $Справочник.Номенклатура]
|    , Остатки.КоличествоОстаток AS Ост
|FROM $РегистрОстатки.ОстаткиТоваров(,,
|        (Товар IN (SELECT Val FROM #Товары))
|        AND (Склад = :вСклад),
|        Товар,
|        Количество) AS Остатки
|";
рс.УложитьСписокОбъектов(СпТовара, "#Товары");
рс.УстановитьТекстовыйПараметр("вСклад", вСклад);

Этот код работает быстро, но он еще не учитывает фильтрацию по реквизиту "Фирма".

Шаг 2: Связывание регистра с журналом документов (_1SJOURN)

Чтобы добраться до реквизита "Фирма", нам нужно соединить таблицу остатков с таблицей журнала документов _1SJOURN. Важный технический нюанс: в 1С 7.7 измерение типа "Документ" в таблицах регистров хранится в 13-символьном виде (ID вида документа + IDDOC). В то же время поле IDDOC в журнале имеет длину 9 символов.

Для корректного соединения мы будем использовать функцию RIGHT(Документ, 9). Рассмотрим подробнее, как встроить это соединение прямо в параметры виртуальной таблицы для максимальной производительности.

Шаг 3: Оптимизированный запрос с фильтрацией по общему реквизиту

Проанализируем итоговое решение, которое позволяет отсечь ненужные данные еще на этапе формирования выборки SQL-сервером. Мы будем использовать INNER JOIN внутри описания виртуальной таблицы и добавим хинт WITH (NOLOCK) для предотвращения блокировок.


|SELECT Остатки.Товар [Товар $Справочник.Номенклатура]
|    , Остатки.Склад [Склад $Справочник.МестаХранения]
|    , Остатки.Документ [Документ $Документ]
|    , Остатки.КоличествоОстаток
|    , Журнал.$ОбщийРеквизит.Фирма [Фирма $Справочник.Фирмы]
|FROM $РегистрОстатки.ОстаткиТоваров(,
|    INNER JOIN _1SJOURN AS Журнал WITH (NOLOCK) ON RIGHT(Документ, 9) = Журнал.IDDOC,
|    (Журнал.$ОбщийРеквизит.Фирма = :ВыбФирма),
|    (Товар, Склад, Документ),
|    (Количество)) AS Остатки

Разберем по шагам, что здесь происходит:

  1. Макрос $РегистрОстатки: Мы передаем параметры фильтрации непосредственно в него. Это критически важно для производительности.
  2. INNER JOIN внутри макроса: Мы связываем каждую запись итогов или движений с журналом документов по идентификатору IDDOC.
  3. Фильтрация по реквизиту: Условие Журнал.$ОбщийРеквизит.Фирма = :ВыбФирма выполняется на стороне SQL, и в 1С возвращаются только те строки, которые соответствуют выбранной фирме.
  4. Типизация: Конструкции вида [Товар $Справочник.Номенклатура] автоматически превращают SQL-идентификаторы в объекты 1С, с которыми удобно работать в коде.

Важные замечания по оптимизации

1. Использование таблиц отбора (_1CRCDOC)

Если для реквизита "Фирма" в Конфигураторе установлен флаг "Отбор по реквизиту", то вместо соединения с тяжелой таблицей _1SJOURN можно использовать таблицу _1CRCDOC. Она гораздо "уже" и содержит только индексы документов в разрезе значений реквизитов. Это может ускорить запрос в несколько раз на очень больших базах данных.

2. Проблема измерения "Документ"

Выясним причину, почему регистры с измерением типа "Документ" со временем начинают тормозить. Когда документом закрывается партия, запись в таблице итогов (RG*) должна обнулиться. Однако, если в базе много "хвостов" (копеечные остатки или незакрытые партии), таблица итогов разрастается до миллионов записей. Прямой запрос помогает с этим справиться, но рекомендуется также проводить регулярную свертку или очистку нулевых остатков.

3. Хинт WITH (NOLOCK)

Мы настоятельно рекомендуем использовать WITH (NOLOCK) во всех JOIN-ах к таблицам 1С. В 1С 7.7 штатные механизмы часто блокируют таблицы целиком при проведении документов. Использование NOLOCK позволяет вашему отчету считывать данные, не дожидаясь окончания транзакций записи, что исключает ошибки типа "Lock time-out".

4. Уход от лишних группировок

Обратите внимание, что в итоговом коде мы убрали GROUP BY из текста SQL-запроса. Это связано с тем, что виртуальная таблица 1С++ сама по себе является агрегирующей и возвращает уже сгруппированные данные по указанным в параметрах измерениям.

Заключение

Переход на прямые SQL-запросы в 1С 7.7 — это основной способ вдохнуть новую жизнь в старую систему. Соединение регистров с журналом документов позволяет гибко фильтровать данные по любым критериям, сохраняя при этом высокую скорость работы. Проанализируйте свои самые медленные отчеты и попробуйте переписать их, используя предложенную схему с INNER JOIN внутри макроса виртуальной таблицы — результат вас приятно удивит.

← На главную