Замедление работы 1С:Предприятие часто связано не с аппаратными ограничениями, а с неэффективными запросами к базе данных. Даже на мощном сервере плохо написанный запрос может блокировать таблицы на минуты, вызывая «подвисания» у десятков пользователей. По данным исследований, 80% проблем производительности в 1С решаются оптимизацией именно запросов — без покупки нового «железа».

В этой статье разберём конкретные методы диагностики и ускорения: от анализа планов выполнения до использования виртуальных таблиц и индексов. Особое внимание уделим типичным ошибкам, которые разработчики допускают при работе с Запрос и ОбъектЗапроса, а также нюансам для разных СУБД (MS SQL, PostgreSQL, IBM DB2). Материал будет полезен как администраторам баз данных, так и 1С-программистам, которые хотят писать код, не тормозящий систему.

1. Диагностика «узких мест»: как найти проблемные запросы

Прежде чем оптимизировать, нужно выявить, какие именно запросы тормозят систему. В 1С:Предприятие 8.3 для этого есть встроенные инструменты, но их часто используют неэффективно. Начнём с базовых методов:

  • 🔍 Журнал регистрации: включите запись событий с уровнем детализации «Подробно» (раздел Администрирование → Журнал регистрации). Ищите запросы с временем выполнения >1 секунды — это потенциальные кандидаты на оптимизацию.
  • ⏱️ Технологический журнал: если у вас MS SQL, настройте сбор событий SQL:BatchCompleted и WaitTypes через SQL Server Profiler. Для PostgreSQL используйте pg_stat_statements.
  • 📊 Монитор производительности 1С: в конфигураторе откройте Сервис → Монитор производительности. Здесь видно не только время выполнения, но и количество блокировок, которые запрос накладывает на таблицы.

Важно: если в журнале регистрации вы видите запрос с временем выполнения 500 мс, но он выполняется 1000 раз в день — его оптимизация даст больший эффект, чем запроса с временем 5 секунд, но выполняемого раз в неделю. Приоритезируйте по суммарной нагрузке.

📊 Какую СУБД вы используете с 1С?
MS SQL Server
PostgreSQL
IBM DB2
Oracle
Другую

Для глубокой диагностики подключитесь напрямую к СУБД. Например, в MS SQL выполните запрос:

SELECT TOP 10

qs.total_elapsed_time/qs.execution_count AS [Avg Duration],

SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,

((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(qt.text)

ELSE qs.statement_end_offset END - qs.statement_start_offset)/2)+1) AS [Query Text],

qs.execution_count,

qs.total_logical_reads/qs.execution_count AS [Avg Reads],

qs.total_logical_writes/qs.execution_count AS [Avg Writes]

FROM sys.dm_exec_query_stats AS qs

CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt

ORDER BY [Avg Duration] DESC;

⚠️ Внимание: Если вы используете 1С:Управление торговлей 11.4 или 1С:ERP 2.5, в этих конфигурациях по умолчанию включены дополнительные индексы для аналитических отчётов. Их отключение может ускорить транзакционные операции, но замедлить формирование отчётности. Проверьте нагрузку в пиковые часы (обычно с 9:00 до 11:00).

2. Оптимизация структуры запроса: от WHERE до JOIN

Самая распространённая ошибка — избыточные соединения таблиц (JOIN). Многие разработчики добавляют LEFT JOIN «на всякий случай», не понимая, как это влияет на план выполнения. Правила оптимизации:

  • 🚫 Избегайте SELECT : всегда указывайте только нужные поля. Например, вместо ВЫБРАТЬ ИЗ Документ.ЗаказПокупателя пишите ВЫБРАТЬ Дата, Номер, СуммаДокумента.
  • 🔄 Заменяйте LEFT JOIN на INNER JOIN, если вам не нужны записи без связей. Это сокращает объём обрабатываемых данных.
  • 📌 Переносите фильтры в WHERE: условия по связанным таблицам лучше указывать прямо в основном запросе, а не в подзапросах.
  • 🗑️ Удаляйте неиспользуемые подзапросы: часто в коде остаются фрагменты вида ГДЕ Сумма(ВЫБРАТЬ 1 ИЗ Таблица ГДЕ ...) > 0, которые можно заменить на СУЩЕСТВУЕТ.

Пример плохого запроса (типичная ошибка новичков):

ВЫБРАТЬ

Заказы.Номер КАК НомерЗаказа,

Клиенты.Наименование КАК Клиент,

Товары.Наименование КАК Товар

ИЗ

Документ.ЗаказПокупателя КАК Заказы

ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Клиенты

ПО Заказы.Контрагент = Клиенты.Ссылка

ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК ТоварыЗаказа

ПО ТоварыЗаказа.Ссылка = Заказы.Ссылка

ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Товары

ПО ТоварыЗаказа.Номенклатура = Товары.Ссылка

ГДЕ

Заказы.Дата МЕЖДУ &НачалоПериода И &КонецПериода

После оптимизации:

ВЫБРАТЬ

Заказы.Номер КАК НомерЗаказа,

Клиенты.Наименование КАК Клиент,

Товары.Наименование КАК Товар

ИЗ

Документ.ЗаказПокупателя КАК Заказы

ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Клиенты

ПО Заказы.Контрагент = Клиенты.Ссылка

ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК ТоварыЗаказа

ПО ТоварыЗаказа.Ссылка = Заказы.Ссылка

ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Товары

ПО ТоварыЗаказа.Номенклатура = Товары.Ссылка

ГДЕ

Заказы.Дата МЕЖДУ &НачалоПериода И &КонецПериода

И НЕ Заказы.ПометкаУдаления

💡

Замена LEFT JOIN на INNER JOIN может ускорить запрос в 2–3 раза, если вам не нужны записи без связей.

3. Индексы: как заставить СУБД работать быстрее

Индексы — это ключ к ускорению запросов, но их неправильное использование может, наоборот, замедлить систему. Основные правила:

  • 🎯 Создавайте индексы для полей в WHERE, JOIN и ORDER BY. Например, если вы часто фильтруете заказы по дате, добавьте индекс на поле Дата.
  • 🔄 Обновляйте статистику индексов: в MS SQL выполните EXEC sp_updatestats, в PostgreSQLANALYZE.
  • 🚫 Не индексируйте поля с низкой селективностью (например, ПометкаУдаления или ЭтоГруппа). Индекс по такому полю не ускорит поиск.
  • 📊 Используйте составные индексы для часто используемых комбинаций полей. Например, если вы всегда фильтруете по Дата и Контрагент, создайте индекс (Дата, Контрагент).

В 1С:Предприятие индексы можно создавать:

  1. Через конфигуратор: откройте свойства таблицы (например, Документ.ЗаказПокупателя) и на закладке Индексы добавьте нужные поля.
  2. Через SQL: для MS SQL выполните:
    CREATE INDEX IX_Заказы_Дата ON dbo._Document123 (Дата)

    (где 123 — числовой идентификатор документа в метаданных).

Тип индекса Когда использовать Пример для 1С
Кластерный Для первичных ключей (автоматически создаётся 1С) _IDRRef в таблицах документов
Некластерный Для часто фильтруемых полей Индекс по полю Номер в справочнике Контрагенты
Составной Для комбинаций полей в WHERE (Дата, Контрагент, Склад) для отчётов по продажам
Покрывающий Когда индекс содержит все нужные поля запроса Индекс (Дата, СуммаДокумента) для запроса ВЫБРАТЬ Дата, СуммаДокумента
⚠️ Внимание: В 1С:Бухгалтерии 3.0 при обновлении на версии старше 3.0.125 автоматически создаются индексы для регистров бухгалтерии. Их ручное удаление может привести к ошибкам при проведении документов. Перед изменением индексов сделайте резервную копию и проверьте в тестовой базе.

4. Виртуальные таблицы: скрытый инструмент ускорения

Виртуальные таблицы — это специальные представления данных, которые 1С формирует «на лету». Они позволяют получать агрегированные данные (например, остатки или обороты) без ручного написания сложных запросов. Их использование может уменьшить нагрузку на СУБД в 5–10 раз.

Основные виды виртуальных таблиц:

  • 📈 Остатки: РегистрНакопления.ТоварыНаСкладах.Остатки() — возвращает остатки на указанную дату.
  • 🔄 Обороты: РегистрНакопления.Продажи.Обороты() — суммы операций за период.
  • 📊 ОстаткиИОбороты: комбинированный вариант для отчётов.
  • 🗂️ СрезПоследних: РегистрСведений.ЦеныНоменклатуры.СрезПоследних() — актуальные данные на дату.

Пример использования виртуальной таблицы вместо ручного запроса:

Плохо (ручной запрос с группировкой):

ВЫБРАТЬ

ТоварыНаСкладах.Номенклатура КАК Товар,

СУММА(ТоварыНаСкладах.Количество) КАК Остаток

ИЗ

Документ.ОприходованиеТоваров.Товары КАК ТоварыНаСкладах

ГДЕ

ТоварыНаСкладах.Документ.Дата <= &ТекущаяДата

СГРУППИРОВАТЬ ПО

ТоварыНаСкладах.Номенклатура

Хорошо (виртуальная таблица):

ВЫБРАТЬ

ОстаткиТоваров.Номенклатура КАК Товар,

ОстаткиТоваров.КоличествоОстаток КАК Остаток

ИЗ

РегистрНакопления.ТоварыНаСкладах.Остатки(&ТекущаяДата, ) КАК ОстаткиТоваров

Преимущества виртуальных таблиц:

  • Готовая оптимизация: 1С сама выбирает самый эффективный план выполнения.
  • 📉 Меньше нагрузки на СУБД: не нужно группировать данные вручную.
  • 🔄 Автоматическое обновление: при изменении данных в регистре виртуальная таблица всегда актуальна.
Когда НЕ стоит использовать виртуальные таблицы?

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

5. Планы выполнения: как их читать и исправлять

План выполнения запроса — это «карта», по которой СУБД ищет данные. Если план неоптимален, даже простой запрос может работать медленно. В 1С:Предприятие план можно получить так:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Заказы.Номер КАК НомерЗаказа

|ИЗ

| Документ.ЗаказПокупателя КАК Заказы

|ГДЕ

| Заказы.Дата МЕЖДУ &НачалоПериода И &КонецПериода";

План = Запросы.ПолучитьПланВыполнения(Запрос);

Что искать в плане:

  • 🔍 Table Scan или Clustered Index Scan: означает, что СУБД сканирует всю таблицу. Решение — добавить индекс.
  • 🔄 Hash Match или Nested Loops: сложные соединения. Попробуйте упростить запрос или добавить индексы.
  • 📊 Sort в середине плана: сортировка больших объёмов данных. Перенесите ORDER BY в подзапрос или добавьте индекс.
  • ⚠️ Spool или Lazy Spool: временные таблицы. Это признак неэффективного плана.

Пример плохого плана (из MS SQL):

Если вы видите:

|--Clustered Index Scan(OBJECT:([dbo].[_Document123] AS [Заказы]))

|--Hash Match(Inner Join, HASH:([Клиенты].[Ссылка])=([Заказы].[Контрагент]))

|--Clustered Index Scan(OBJECT:([dbo].[_Reference14] AS [Клиенты]))

Это значит, что:

  1. СУБД сканирует всю таблицу заказов (Clustered Index Scan).
  2. Использует дорогое соединение Hash Match.

Решение: добавьте индексы на поля Дата и Контрагент в таблице заказов.

☑️ Проверка плана выполнения

Выполнено: 0 / 4

6. Типичные ошибки и как их избежать

Даже опытные разработчики допускают ошибки, которые тормозят систему. Вот самые распространённые:

  • 🔄 Подзапросы в WHERE:

    Плохо: ГДЕ Сумма(ВЫБРАТЬ 1 ИЗ Документ.ЗаказПокупателя ГДЕ Контрагент = &Контрагент) > 0

    Хорошо: ГДЕ СУЩЕСТВУЕТ (ВЫБРАТЬ 1 ИЗ Документ.ЗаказПокупателя ГДЕ Контрагент = &Контрагент)

  • 📊 Функции над полями в WHERE:

    Плохо: ГДЕ ЛЕВ(Заказы.Номер, 3) = "АБВ" (индекс не используется)

    Хорошо: ГДЕ Заказы.Номер LIKE "АБВ%"

  • 🗑️ Избыточные данные в SELECT:

    Плохо: ВЫБРАТЬ Заказы.* (выбираются все поля, включая ненужные)

    Хорошо: ВЫБРАТЬ Заказы.Номер, Заказы.Дата, Заказы.СуммаДокумента

  • ⏱️ Отсутствие ограничений по дате:

    Плохо: ВЫБРАТЬ * ИЗ Документ.ЗаказПокупателя (сканирует всю таблицу)

    Хорошо: ВЫБРАТЬ * ИЗ Документ.ЗаказПокупателя ГДЕ Дата >= ДобавитьМесяц(ТекущаяДата(), -1)

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

  • 🔒 Используйте ДЛЯ ИЗМЕНЕНИЯ только там, где действительно нужно изменять данные.
  • 📌 Разбивайте большие транзакции на более мелкие.
  • ⚡ Уменьшайте время выполнения запроса (см. предыдущие разделы).
⚠️ Внимание: В 1С:Зарплата и управление персоналом 3.1 при расчёте зарплаты за месяц часто возникают блокировки на регистре РасчетыСотрудников. Если расчёт «зависает», проверьте, не держит ли кто-то транзакцию открытой (например, не закрыто окно документа).

7. Оптимизация для конкретных СУБД: MS SQL vs PostgreSQL

Разные СУБД по-разному оптимизируют запросы. Что работает в MS SQL, может быть неэффективно в PostgreSQL, и наоборот.

Параметр MS SQL Server PostgreSQL
Индексы Поддерживает включённые столбцы (INCLUDE) Нет включённых столбцов, но есть частичные индексы (WHERE в определении)
Планы выполнения Использует SET SHOWPLAN_TEXT ON Использует EXPLAIN ANALYZE
Ограничение выборки TOP 100 или FETCH FIRST 100 ROWS ONLY LIMIT 100
Работа с датами Функции DATEADD, DATEDIFF Операторы + interval, DATE_PART
Транзакции Поддерживает READ COMMITTED SNAPSHOT Использует MVCC (Multi-Version Concurrency Control)

Примеры оптимизации для разных СУБД:

Для MS SQL:

  • 📌 Используйте WITH (NOLOCK) для запросов, где не важна 100% актуальность данных (например, отчёты):
ВЫБРАТЬ

Заказы.Номер КАК НомерЗаказа

ИЗ

Документ.ЗаказПокупателя КАК Заказы WITH (NOLOCK)

ГДЕ

Заказы.Дата = &ТекущаяДата

Для PostgreSQL:

  • 📊 Используйте EXPLAIN ANALYZE для детального анализа:
EXPLAIN ANALYZE

SELECT "Номер" AS "НомерЗаказа"

FROM "_Document123" AS "Заказы"

WHERE "Дата" = '2023-10-01';

В PostgreSQL также полезно настраивать параметры work_mem и shared_buffers в postgresql.conf, если у вас много сложных запросов с сортировкой.

💡

В PostgreSQL для ускорения запросов с LIKE используйте расширение pg_trgm. Оно позволяет создавать индексы для поиска по части строки (например, WHERE Наименование LIKE '%текст%').

8. Автоматизация оптимизации: инструменты и скрипты

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

  • 🔧 1С:Анализ производительности: встроенный инструмент в конфигураторе (Сервис → Анализ производительности). Показывает «тяжёлые» запросы и предлагает варианты оптимизации.
  • 📊 SQL Diagnostic Manager (для MS SQL): мониторит производительность в реальном времени и предлагает улучшения.
  • 🤖 Скрипты для автоматического создания индексов:

    Пример для MS SQL (ищет отсутствующие индексы):

    SELECT
    

    migs.avg_total_user_cost (migs.avg_user_impact / 100.0) (migs.user_seeks + migs.user_scans) AS [IndexAdvantage],

    'CREATE INDEX [IX_' + OBJECT_NAME(migs.object_id) + '_' +

    REPLACE(REPLACE(REPLACE(ISNULL(mid.equality_columns, ''), ', ', '_'), '[', ''), ']', '') +

    CASE WHEN migs.group_handle > 0 THEN '_' + CAST(migs.group_handle AS VARCHAR) ELSE '' END +

    '] ON ' + migs.statement +

    ' (' + ISNULL(mid.equality_columns, '') +

    CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN ',' ELSE '' END +

    ISNULL(mid.inequality_columns, '') + ')' +

    ISNULL(' INCLUDE (' + mid.included_columns + ')', '') AS [CreateIndexStatement],

    migs.*

    FROM

    sys.dm_db_missing_index_groups mig

    INNER JOIN sys.dm_db_missing_index_group_stats migs ON mig.index_group_handle = migs.group_handle

    INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle

    WHERE

    migs.avg_total_user_cost (migs.avg_user_impact / 100.0) (migs.user_seeks + migs.user_scans) > 1000

    ORDER BY

    [IndexAdvantage] DESC;

  • 🔄 1С:Центр управления производительностью: отдельный продукт для мониторинга и оптимизации кластеров 1С.

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

  • Еженедельно обновлять статистику индексов.
  • Ежедневно чистить журнал регистрации (если он разросся до гигабайтов).
  • Ежемесячно проверять фрагментацию индексов (для MS SQL).
💡

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

Не забывайте про тестирование: перед применением изменений на рабочей базе проверяйте их в тестовом контуре. Особенно это касается:

  • Создания/удаления индексов.
  • Изменения структуры таблиц.
  • Обновления конфигурации.
⚠️ Внимание: В 1С:Комплексная автоматизация 2.4 при обновлении на версии старше 2.4.10 автоматически включается механизм Управляемые блокировки. Он может конфликтовать с ручными блокировками в запросах. Если после обновления появились «зависания», проверьте настройки блокировок в Администрирование → Настройки программы → Производительность.

FAQ: Частые вопросы по оптимизации запросов в 1С

❓ Как узнать, какой именно запрос тормозит систему?

Используйте технологический журнал (для 1С) или SQL Profiler (для MS SQL). Включите запись событий с фильтром по времени выполнения (>1000 мс). Также поможет встроенный Монитор производительности в конфигураторе.

❓ Почему запрос работает быстро в тестовой базе, но медленно на рабочей?

Причины могут быть разные:

  • 📊 Объём данных: в рабочей базе больше записей, и план выполнения меняется.
  • 🔄 Блокировки: другие пользователи могут блокировать таблицы.
  • 🗑️ Фрагментация индексов: