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

В этой статье мы разберем одну из таких типичных, но неприятных проблем, которая серьезно влияет на быстродействие системы, и покажем, как ее решить.
Мы поэтапно разберем кейс: от поиска "тормозящего" запроса до готового решения. Вы поймете:
- Почему один и тот же запрос в разных базах работает с разной скоростью.
- Как данные вашей компании влияют на эффективность индексов.
- К каким последствиям приводит пренебрежение стандартными рекомендациями от 1С.
Поиск проблемы
Для выявления узких мест мы используем режим свободного поиска, который позволяет находить запросы, превышающие заданное время выполнения.
Порядок действий:
- В конфигурации мониторинга производительности откройте журнал событий замеров.
- Выберите замер типа «Длительные запросы — 1С & Postgres».
- Примените фильтр по времени выполнения, установив пороговое значение в 60 секунд.

Рис. 1 — Динамический список событий замеров. Выбор замера длительных запросов
Среди результатов отбора мы выделим для детального анализа наиболее показательный случай — запрос, выполнявшийся 987 секунд (более 16 минут). Столь длительное выполнение явно указывает на серьезную проблему, с которой столкнулся пользователь под номером 108.
Система.ПолучитьФорму : Обработка.ЗаполнениеКорректировкиНазначения.Форма.ФормаОбъекта
Обработка.ЗаполнениеКорректировкиНазначения.Форма.ФормаОбъекта.Форма : 116 : ЗаполнитьТаблицу();
Обработка.ЗаполнениеКорректировкиНазначения.Форма.ФормаОбъекта.Форма : 2024 : Назначения.ВыгрузитьЗначения()
Документ.КорректировкаНазначенияТоваров.МодульМенеджера : 1046 : ТоварыОрганизаций = ДавальческиеТоварыОрганизацийПоОтбору(Строки, Регистратор);
Документ.КорректировкаНазначенияТоваров.МодульМенеджера : 4990 : Результат = Запрос.Выполнить().Выгрузить();
Итак, мы быстро локализовали точку возникновения проблемы. Теперь перейдем к анализу причин.
Анализ проблемы
Локализация исходного кода
В конфигураторе открываем модуль менеджера документа «Корректировка назначения товаров» и находим строку 4990, где содержится проблемный запрос.
Идентификация "виновника"
Мы видим пакет запросов. Чтобы найти среди них целевой, анализируем их структуру через SQL-представление.

Рис. 2 SQL-представление пакета запросов
Анализ показывает, что только второй запрос в пакете соответствует искомой логике: он соединяет временную таблицу, справочник и регистр сведений. Мы нашли виновника замедления.
Подготовка к тестированию
Копируем первые два запроса пакета для анализа в консоли. Чтобы упростить тестирование, заменяем создание временной таблицы на прямую выборку из табличной части документа «Корректировка назначения товаров».
ВЫБРАТЬ
ВЫРАЗИТЬ(ТаблицаОтбора.ИсходноеНазначение КАК Справочник.Назначения) КАК ИсходноеНазначение,
ТаблицаОтбора.Номенклатура КАК Номенклатура,
ТаблицаОтбора.Характеристика КАК Характеристика,
ТаблицаОтбора.Склад КАК Склад
ПОМЕСТИТЬ ТаблицаОтбора
ИЗ
Документ.КорректировкаНазначенияТоваров.Товары КАК ТаблицаОтбора
ГДЕ
ТаблицаОтбора.Ссылка = &Ссылка
ИНДЕКСИРОВАТЬ ПО
ИсходноеНазначение,
Номенклатура,
Характеристика,
Склад
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТаблицаОтбора.ИсходноеНазначение КАК ИсходноеНазначение,
ТаблицаОтбора.Номенклатура КАК Номенклатура,
ТаблицаОтбора.Характеристика КАК Характеристика,
ТаблицаОтбора.Склад КАК Склад,
ДавальческоеНазначение.Ссылка КАК НазначениеДавальца,
РегистрАналитик.КлючАналитики КАК КлючАналитики
ПОМЕСТИТЬ ТаблицаДавальческихСтрок
ИЗ
ТаблицаОтбора КАК ТаблицаОтбора
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Назначения КАК ДавальческоеНазначение
ПО (ДавальческоеНазначение.Партнер = ТаблицаОтбора.ИсходноеНазначение.Партнер)
И (ДавальческоеНазначение.Договор = ТаблицаОтбора.ИсходноеНазначение.Договор)
И (ДавальческоеНазначение.НаправлениеДеятельности = ТаблицаОтбора.ИсходноеНазначение.НаправлениеДеятельности)
И (ДавальческоеНазначение.ТипНазначения = ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалы22))
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.АналитикаУчетаНоменклатуры КАК РегистрАналитик
ПО (РегистрАналитик.Назначение = ТаблицаОтбора.ИсходноеНазначение)
И (РегистрАналитик.Номенклатура = ТаблицаОтбора.Номенклатура)
И (РегистрАналитик.Характеристика = ТаблицаОтбора.Характеристика)
И (РегистрАналитик.МестоХранения = ТаблицаОтбора.Склад)
ГДЕ
ТаблицаОтбора.ИсходноеНазначение.ТипНазначения В (ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалы22), ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалыПодЭтап22))
ИНДЕКСИРОВАТЬ ПО
КлючАналитики,
ИсходноеНазначение,
Номенклатура,
Характеристика,
Склад
Выполнение и анализ плана запроса
Выполняем подготовленный запрос. Время выполнения составляет ~38 секунд, что подтверждает проблему.
https://explain.tensor.ru/archive/explain/fa52f4cfa2d592302d31e89db165dcef:0:2024-05-21#visio

Рис. 3 Графическое представление плана запроса
Анализ плана выявляет узкое место: оператор Index Scan (Index Seek) для таблицы справочника назначений, потребляющий 99.9% всех ресурсов (выделен красным).
Возникает резонный вопрос: «Почему доступ по индексу, который должен быть оптимальным, оказывается столь медленным?»
Предположение: Планировщик СУБД выбрал неэффективный план из-за неверной оценки селективности.
Гипотеза и проверка
Выдвигаем гипотезу: проблема в распределении данных. Проанализируем заполненность справочника назначений, сгруппировав записи по наличию партнера.
Таблица 1. Распределение пустых партнеров в справочнике назначения

Анализ данных и выдвижение гипотез
Как видно из таблицы, в справочнике назначений присутствует аномально высокое количество записей с пустыми партнерами. Это количество — 2 162 574 — в точности совпадает с числом отброшенных строк (Rows Removed by Filter).
- Серая цифра в плане запроса показывает отброшенные за один цикл.
- Красная цифра — это суммарное количество отфильтрованных строк за все циклы.
Приблизительный расчет: 2 162 574 × 29 ≈ 62 714 646 строк были обработаны и отброшенны впустую.
Ключевой вывод: Использование индекса по полю «Партнер» в данной ситуации крайне неэффективно. Это становится особенно очевидно для давальческих материалов, где партнер в демонстрационной базе всегда пустой. Индексное сканирование в этом сценарии дает лишь незначительное преимущество над полным сканированием таблицы.
Поскольку во временной таблице у всех или части назначений партнер пустой, использование этого индекса для таких данных неэффективно. Отсюда и ситуация, когда время выполнения запроса может меняться от миллисекунд до нескольких минут.
Возражение: «Все же планировщик СУБД ошибся!»
Наш ответ: Не будем спешить с выводами. Чтобы дать обоснованный ответ, необходимо продолжить анализ.
Что там про стандарты разработки?
Локализация проблемного оператора
Анализ плана выполнения показывает, что узким местом является оператор сканирования по индексу для таблицы с псевдонимом t3. Сопоставив план с текстом SQL, мы определяем, что это — первое внутреннее соединение во втором запросе. Дополнительным маркером служит условие соединения по полю «Партнер», уникальное для этой части запроса.
Выявление архитектурной ошибки
Здесь мы сталкиваемся с ключевой проблемой. Обратите внимание на синтаксис условия соединения:
ДавальческоеНазначение.Партнер = ТаблицаОтбора.ИсходноеНазначение.Партнер
Использование в условии соединения поля через точку (.) создает неявное соединение, что является грубым отступлением от стандартов разработки 1С. Это классический пример «плохого тона».
Обычно считается, что поля, участвующие в соединениях, должны быть проиндексированы. Но для таблицы «ТаблицаОтбора» невозможно создать индекс по полям через точку (так как их явно нет в ней): «Партнер», «Договор» или «НаправлениеДеятельности».
Однако главная проблема не в этом. На самом деле «настоящий» запрос выглядит иначе. Перепишем его, явно раскрыв все соединения через точку, чтобы увидеть реальную логику.
//...
ИЗ
ТаблицаОтбора КАК ТаблицаОтбора
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.АналитикаУчетаНоменклатуры КАК РегистрАналитик
ПО (РегистрАналитик.Назначение = ТаблицаОтбора.ИсходноеНазначение)
И (РегистрАналитик.Номенклатура = ТаблицаОтбора.Номенклатура)
И (РегистрАналитик.Характеристика = ТаблицаОтбора.Характеристика)
И (РегистрАналитик.МестоХранения = ТаблицаОтбора.Склад)
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Назначения КАК НазначениеЧерезТочку
ПО (ДавальческоеНазначение. ИсходноеНазначение = НазначениеЧерезТочку.Ссылка)
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Назначения КАК ДавальческоеНазначение
ПО (ДавальческоеНазначение.Партнер = НазначениеЧерезТочку.Партнер)
И (ДавальческоеНазначение.Договор = НазначениеЧерезТочку.Договор)
И (ДавальческоеНазначение.НаправлениеДеятельности = ТНазначениеЧерезТочку.НаправлениеДеятельности)
И (ДавальческоеНазначение.ТипНазначения = ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалы22))
ГДЕ
НазначениеЧерезТочку.ТипНазначения В (ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалы22), ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалыПодЭтап22))
//...
И вот неожиданность: нет никакого прямого соединения таблицы отборов с таблицей назначений по полям «Партнер», «Договор» или «НаправлениеДеятельности»!
Замечание! Я расположил порядок соединений таблиц в соответствии с порядком выполнения в плане запроса для наглядности.
Если мы возьмем план запроса с рисунка 3 и проследим за его выполнением (слева направо и сверху вниз), порядок будет таким:
- Сначала выполняется соединение временной таблицы с таблицей аналитик учета номенклатуры
- Затем выполняется соединение с таблицей назначения, которая обращается через точку.
- Последним выполняется соединение с таблицей назначения, где происходит отбор по партнеру, договору, направлению деятельности и типу назначения.
Дополнительные замечания
- Избыточное левое соединение можно заменить на внутреннее, так как условия отбора исключают NULL-значения.
- Несоответствие в фильтрах (разное количество значений для типа назначения в условиях) на результат не влияет, но выглядит неаккуратно.
Вопрос из зала: «Это случайность! Где же мы можем найти еще подобные ошибки?»
Ответ: В модулях расчета себестоимости. Там подобные конструкции наиболее распространены. Мы уже переписали около десяти аналогичных запросов, что на продуктовой базе позволило сократить время выполнения ключевых операций с 16-20 часов до приемлемых значений. Вот где обширное поле для оптимизации!
Мероприятия по устранению проблемы
Решение (Вариант 1): Создание индекса
Давайте посмотрим, какие индексы у нас есть вообще.
Таблица 2. Существующие индексы

А других индексов, которые входили бы в условия соединения, попросту нет! Планировщику не из чего выбирать.
Наиболее простым решением будет создать индекс по полю «ТипНазначения».
Таблица 3. Распределение типов назначения в справочнике назначения

По договорам у нас ситуация аналогична ситуации с партнерами (пустые значения, т.к. партнер пустой).
Таблица 4. Распределение пустых договоров в справочнике назначения

Речь в запросе идет о давальческих материалах. Как мы видим, при отборе по типу назначения выборка ограничивается всего 148 записями, вместо более чем 2 миллионов в случае с партнерами. Если мы добавим индекс по полю «ТипНазначения», запрос будет выполняться быстро, примерно за ~40 мс.
https://explain.tensor.ru/archive/explain/32b6706f373ec971fa1661d51370e3e0:0:2025-11-23#visio

Рис. 4 Графическое представление плана запроса (оптимизация вариант 1)
Система перешла на использование более логичного индекса. Реализовать это решение оказалось просто — потребовалось лишь изменить свойство поля «ТипНазначения» на «Индексировать».
Однако получившийся индекс не является оптимальным с точки зрения состава данных. Согласитесь, индекс, построенный только на одном перечислении с 5-6 значениями на миллионы записей — не лучшая идея.
Несмотря на рост производительности, это решение выглядит неидеальным. Рассмотрим другой вариант.
Решение (Вариант 2): Декомпозиция запроса
Если изучить план выполнения, ключевая проблема очевидна: проблемный оператор обращения к индексу повторяется 29 раз, и ни одна строка не проходит проверку. Это указывает на неэффективность логики соединения.

Рис. 4 Свойства оператора Index Scan, потребляющего 99.9% ресурсов
Суть решения: Мы проводим декомпозицию (разбиение) исходного запроса на несколько логических этапов. Это позволяет изолировать неоптимальное соединение и предотвратить его многократное выполнение.
План действий:
- Выделяем первую часть из второго запроса пакета.
- Результат её выполнения сохраняем во временную таблицу ТаблицаДавальческихСтрокЧастьПервая.
- Используем эту таблицу в последующих операциях вместо повторного вычисления сложного соединения.
Замечание! В данном примере я не стал явно раскрывать все поля через точку для наглядности подхода. Читатель может выполнить эти операции самостоятельно по аналогии с разбором выше. Фактически с помощью декомпозиции мы снизили сложность поиска оптимального плана для оптимизатора СУБД.
Текст модифицированного запроса:
ВЫБРАТЬ
ВЫРАЗИТЬ(ТаблицаОтбора.ИсходноеНазначение КАК Справочник.Назначения) КАК ИсходноеНазначение,
ТаблицаОтбора.Номенклатура КАК Номенклатура,
ТаблицаОтбора.Характеристика КАК Характеристика,
ТаблицаОтбора.Склад КАК Склад
ПОМЕСТИТЬ ТаблицаОтбора
ИЗ
Документ.КорректировкаНазначенияТоваров.Товары КАК ТаблицаОтбора
ГДЕ
ТаблицаОтбора.Ссылка = &Ссылка
ИНДЕКСИРОВАТЬ ПО
ИсходноеНазначение,
Номенклатура,
Характеристика,
Склад
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТаблицаОтбора.ИсходноеНазначение КАК ИсходноеНазначение,
ТаблицаОтбора.Номенклатура КАК Номенклатура,
ТаблицаОтбора.Характеристика КАК Характеристика,
ТаблицаОтбора.Склад КАК Склад,
ДавальческоеНазначение.Ссылка КАК НазначениеДавальца
ПОМЕСТИТЬ ТаблицаДавальческихСтрокЧастьПервая
ИЗ
ТаблицаОтбора КАК ТаблицаОтбора
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Назначения КАК ДавальческоеНазначение
ПО (ДавальческоеНазначение.Партнер = ТаблицаОтбора.ИсходноеНазначение.Партнер)
И (ДавальческоеНазначение.Договор = ТаблицаОтбора.ИсходноеНазначение.Договор)
И (ДавальческоеНазначение.НаправлениеДеятельности = ТаблицаОтбора.ИсходноеНазначение.НаправлениеДеятельности)
И (ДавальческоеНазначение.ТипНазначения = ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалы22))
ГДЕ
ТаблицаОтбора.ИсходноеНазначение.ТипНазначения В (ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалы22), ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалыПодЭтап22))
ИНДЕКСИРОВАТЬ ПО
ИсходноеНазначение,
Номенклатура,
Характеристика,
Склад
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТаблицаОтбора.ИсходноеНазначение КАК ИсходноеНазначение,
ТаблицаОтбора.Номенклатура КАК Номенклатура,
ТаблицаОтбора.Характеристика КАК Характеристика,
ТаблицаОтбора.Склад КАК Склад,
ТаблицаОтбора.НазначениеДавальца КАК НазначениеДавальца,
РегистрАналитик.КлючАналитики КАК КлючАналитики
ПОМЕСТИТЬ ТаблицаДавальческихСтрок
ИЗ
ТаблицаДавальческихСтрокЧастьПервая КАК ТаблицаОтбора
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.АналитикаУчетаНоменклатуры КАК РегистрАналитик
ПО (РегистрАналитик.Назначение = ТаблицаОтбора.ИсходноеНазначение)
И (РегистрАналитик.Номенклатура = ТаблицаОтбора.Номенклатура)
И (РегистрАналитик.Характеристика = ТаблицаОтбора.Характеристика)
И (РегистрАналитик.МестоХранения = ТаблицаОтбора.Склад)
ГДЕ
ТаблицаОтбора.ИсходноеНазначение.ТипНазначения В (ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалы22), ЗНАЧЕНИЕ(Перечисление.ТипыНазначений.ДавальческоеМатериалыПодЭтап22))
ИНДЕКСИРОВАТЬ ПО
КлючАналитики,
ИсходноеНазначение,
Номенклатура,
Характеристика,
Склад
После применения предложенных мероприятий производительность запроса была кардинально улучшена. Общее время выполнения сократилось с 987 секунд (16.5 минут) до ~5 миллисекунд.
Время выполнения первой части запроса: 1.878 мс
https://explain.tensor.ru/archive/explain/b70180df627fc417e4a0ec358b9ba9bc:0:2024-05-26#visio
Время выполнения второй части запроса: 3.136 мс
https://explain.tensor.ru/archive/explain/9afeafd15e74610fd4c0861f9ed821f3:0:2024-05-26#visio
Ключевое наблюдение: Источник данных по индексу Партнер, который был главной причиной «тормозов», более не используется. Это логично с точки зрения реального наполнения базы данных и подтверждает правильность проведенной декомпозиции.
Заключение: Запрос выполняется практически мгновенно. Важно отметить, что предложенная оптимизация эффективна не только для пустых выборок, но и будет стабильно хорошо работать при наличии данных, так как неэффективные циклы были устранены.
Заключение
В рамках этого разбора мы прошли полный путь оптимизации: от поиска медленного запроса до внедрения решения и проверки результата.
Ключевые итоги:
- Оптимизация требует контекста. Идеальное решение на этапе разработки может оказаться провальным на реальной базе с уникальным распределением данных. Эффективная оптимизация всегда привязана к конкретному состоянию базы и актуальным планам запросов.
- Избегайте отклонений от стандартов. Если вы видите в секции соединения условия с соединениями через точку (.),
то за такой запрос я бы предложил бить по рукам разработчику большой желтой книжкой со стандартамиэто повод немедленно выполнить рефакторинг такого кода. - Декомпозиция — мощное оружие. Упрощение сложных запросов путем разбиения их на логические этапы часто оказывается самым эффективным решением. Это снижает нагрузку на оптимизатор СУБД и помогает ему выбрать по-настоящему оптимальную стратегию выполнения.
- Не существует единственно верного пути. Работа с производительностью — это не магия, а системный процесс поиска «узких мест», основанный на фактах, а не на предположениях. Используйте рассмотренные методики, и ваши системы будут работать значительно стабильнее и быстрее.

Вступайте в нашу телеграмм-группу Инфостарт