Почему запрос "Состояние клиентов по групповым занятиям" в 1С стал выполняться очень медленно и как его оптимизировать?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

В работе с системами 1С нередко возникает ситуация, когда ранее быстро работающие запросы начинают значительно замедляться. Одна из частых причин — это рост объема данных в информационной базе, что особенно заметно после интенсивных периодов работы, например, после нового года, когда накапливается критическая масса записей. Мы рассмотрим подобную проблему, возникшую в конфигурации 1С:Фитнес, связанную с запросом, определяющим состояние клиентов по групповым занятиям, и предложим подробные шаги по её оптимизации.

Анализируем исходную проблему: медленный запрос в 1С:Фитнес

Пользователь столкнулся с тем, что запрос СформироватьТаблицаСостояниеКлиентовПоГрупповымЗанятиям в его конфигурации 1С:Фитнес, работающей на файловой базе (платформа 15), стал выполняться до 70 секунд. Этот запрос критичен, поскольку запускается при каждом "впуске" или "выпуске" клиента из клуба. Задержка в 70 секунд для такой операции неприемлема и серьезно затрудняет работу.

Рассмотрим исходный текст проблемной процедуры и запроса:


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

Основная проблема этого запроса кроется в использовании виртуальной таблицы РегистрСведений.СостояниеКлиентовПоГрупповымЗанятиям.СрезПоследних(). Виртуальная таблица СрезПоследних, при вызове без указания параметров отбора по измерениям (таких как Контрагент, СтруктурнаяЕдиница, ГрупповоеЗанятие), вынуждена сначала сформировать срез последних записей по всем измерениям для всего регистра. Только после этого к результату применяется отбор по Регистратор <> &Регистратор, а затем происходит соединение с ТаблицаСостояниеКлиентовПоГрупповымЗанятиям. Когда регистр сведений накапливает большое количество записей (например, десятки или сотни тысяч), эта операция становится крайне затратной по времени и ресурсам.

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

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

Шаги по оптимизации запроса

Основываясь на обсуждении и принципах оптимизации запросов в 1С, мы предлагаем несколько подходов для значительного ускорения работы.

1. Максимально сужаем область выборки в виртуальной таблице "СрезПоследних"

Это наиболее важный и эффективный шаг. Цель состоит в том, чтобы передать параметры отбора по измерениям непосредственно в вызов виртуальной таблицы СрезПоследних. Если мы заранее знаем, для каких конкретных клиентов, структурных единиц или групповых занятий нам нужен срез, мы должны указать это в параметрах виртуальной таблицы. В данном случае, поскольку запрос выполняется для "впуска/выпуска" одного человека, скорее всего, нам нужен срез для одного конкретного Контрагента (клиента).

Мы должны получить значения Контрагент, СтруктурнаяЕдиница, ГрупповоеЗанятие из временной таблицы ТаблицаСостояниеКлиентовПоГрупповымЗанятиям и передать их в параметры виртуальной таблицы СрезПоследних.

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


Процедура СформироватьТаблицаСостояниеКлиентовПоГрупповымЗанятиям(СтруктураДополнительныеСвойства)
    Запрос = Новый Запрос;
    Запрос.МенеджерВременныхТаблиц = СтруктураДополнительныеСвойства.ДляПроведения.СтруктураВременныеТаблицы.МенеджерВременныхТаблиц;

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

В этом улучшенном варианте мы сначала формируем временную таблицу ВТ_ПараметрыСреза, которая содержит уникальные комбинации измерений, по которым нам действительно нужен срез. Затем эти измерения используются в операторе В внутри параметров виртуальной таблицы СрезПоследних. Это позволяет 1С сразу строить срез по гораздо меньшему набору данных, что значительно ускоряет выполнение запроса. Именно такой подход позволил автору темы сократить время выполнения запроса с 70 секунд до практически нуля.

Если же мы абсолютно уверены, что ТаблицаСостояниеКлиентовПоГрупповымЗанятиям всегда содержит только одну запись для одного клиента, одной структурной единицы и одного группового занятия, то можно обойтись без промежуточной временной таблицы ВТ_ПараметрыСреза. В этом случае мы можем получить значения параметров непосредственно из временной таблицы в коде 1С перед формированием текста запроса, а затем передать их в параметры запроса СрезПоследних:


Процедура СформироватьТаблицаСостояниеКлиентовПоГрупповымЗанятиям(СтруктураДополнительныеСвойства)
    // Предполагаем, что в ТаблицаСостояниеКлиентовПоГрупповымЗанятиям всегда 1 запись.
    // Если это не так, используйте вариант с ВТ_ПараметрыСреза выше.
    Если СтруктураДополнительныеСвойства.ТаблицыДляДвижений["ТаблицаСостояниеКлиентовПоГрупповымЗанятиям"].Количество() = 1 Тогда
        СтрокаТЗ = СтруктураДополнительныеСвойства.ТаблицыДляДвижений["ТаблицаСостояниеКлиентовПоГрупповымЗанятиям"][0];
        Перем_Контрагент = СтрокаТЗ.Контрагент;
        Перем_СтруктурнаяЕдиница = СтрокаТЗ.СтруктурнаяЕдиница;
        Перем_ГрупповоеЗанятие = СтрокаТЗ.ГрупповоеЗанятие;
    Иначе
        // Обработка ситуации, когда клиентов несколько или их нет, 
        // или если исходная логика процедуры подразумевает другой способ получения параметров.
        // Для демонстрации примера мы просто прервем выполнение.
        Возврат; 
    КонецЕсли;

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

2. Анализируем и оптимизируем временную таблицу ТаблицаСостояниеКлиентовПоГрупповымЗанятиям

Перед тем как соединять срез последних с основной таблицей, важно убедиться, что сама ТаблицаСостояниеКлиентовПоГрупповымЗанятиям уже содержит минимально необходимый объем данных. Если эта таблица формируется из нескольких источников или содержит избыточные записи, это может быть дополнительным источником замедления.

  1. Проверяем индексацию полей: Для эффективной работы соединений и отборов, ключевые поля во временной таблице, по которым происходит соединение с другими таблицами или виртуальными таблицами, должны быть проиндексированы. В данном случае это СтруктурнаяЕдиница, ГрупповоеЗанятие, Контрагент. При создании временной таблицы мы можем явно указать, какие поля индексировать. Это критично для файловых баз, где СУБД не может сама управлять индексами.
  2. Минимизируем количество записей: Убедитесь, что процесс, который формирует ТаблицаСостояниеКлиентовПоГрупповымЗанятиям, не добавляет в нее лишних записей. Чем меньше записей в этой таблице, тем быстрее будет выполняться весь запрос.

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

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

  1. Сначала формируем временную таблицу (например, ВТ_ПараметрыСреза), содержащую уникальные комбинации измерений, по которым нам нужно получить срез. Это позволяет сократить количество параметров для следующего шага.
  2. Затем, на основе ВТ_ПараметрыСреза, мы формируем другую временную таблицу (например, ВТ_СрезСостояниеКлиентов), которая содержит уже отфильтрованный и "облегченный" срез данных из регистра сведений, используя ранее полученные параметры.
  3. И только после этого мы соединяем основную ТаблицаСостояниеКлиентовПоГрупповымЗанятиям с этой уже оптимизированной и небольшой временной таблицей ВТ_СрезСостояниеКлиентов.

Пример такого пакета запросов:


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

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

4. Общие рекомендации по оптимизации запросов и обслуживанию файловых баз

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

  1. Актуальность индексов регистров: Мы должны убедиться, что все измерения регистра сведений СостояниеКлиентовПоГрупповымЗанятиям (СтруктурнаяЕдиница, Контрагент, ГрупповоеЗанятие), а также другие поля, участвующие в соединениях и отборах, проиндексированы в конфигурации. Корректные индексы значительно ускоряют поиск и соединение данных.
  2. Регулярное тестирование и исправление ИБ: Для файловых баз критически важно периодически выполнять "Тестирование и исправление информационной базы" через утилиту chdbfl.exe (или из режима Конфигуратора). Особое внимание уделяем опциям "Пересчет итогов" и "Сжатие таблиц". Эти операции помогают устранять логические ошибки, оптимизировать физическое размещение данных и восстанавливать оптимальную производительность.
  3. Минимизация выбираемых полей: Мы должны выбирать из запроса только те поля, которые действительно необходимы для дальнейшей обработки или отображения. Использование оператора РАЗЛИЧНЫЕ в сочетании с большим количеством выбираемых полей может приводить к созданию объемных промежуточных таблиц и замедлению выполнения.
  4. Анализ объема данных в регистрах: Если регистр сведений, подобно СостояниеКлиентовПоГрупповымЗанятиям, накапливает очень большое количество записей, при этом для работы всегда нужна только "последняя" запись, стоит задуматься о пересмотре структуры регистра или алгоритма записи данных. Возможно, часть старых данных можно архивировать или удалять, чтобы уменьшить общий объем, требующий обработки.
  5. Границы рассчитанных итогов (для регистров накопления): Хотя в данном случае речь идет о регистре сведений, для регистров накопления важно следить за корректной настройкой границ рассчитанных итогов. Неоптимальные границы могут приводить к пересчету слишком больших объемов данных при обращении к итогам.

Вывод

Проблема медленного выполнения запросов в 1С, особенно связанных с виртуальными таблицами типа СрезПоследних, чаще всего возникает из-за неэффективной выборки данных из больших регистров. Ключевым решением является максимальное ограничение объема данных, обрабатываемых виртуальными таблицами, путем добавления отборов по измерениям непосредственно при их вызове. Использование пакетных запросов с временными таблицами для предварительной фильтрации или агрегации данных также является мощным инструментом оптимизации. Для файловых баз данных критически важно уделять внимание индексам и регулярному обслуживанию информационной базы.

Применив описанные методы, мы не только устраним текущие задержки, но и обеспечим более стабильную и быструю работу вашей системы 1С:Фитнес в долгосрочной перспективе, даже при дальнейшем росте объема данных.

← На главную