Мы часто сталкиваемся с ситуацией, когда, казалось бы, простой запрос в 1С:Предприятии начинает выполняться недопустимо долго, замедляя работу всей системы. Это может быть вызвано различными причинами: от неоптимального проектирования регистров до неправильного использования синтаксиса языка запросов. В этой статье мы вместе разберем основные методы и подходы к оптимизации медленных запросов, основываясь на практическом опыте и рекомендациях специалистов.
Давайте пошагово рассмотрим, как мы можем улучшить производительность наших запросов и избежать типовых ошибок.
Индексы — это фундамент быстрой работы с данными. Они значительно ускоряют поиск и фильтрацию записей, особенно в больших таблицах, и снижают негативное влияние блокировок в многопользовательских системах.
Автоматические и явные индексы:
Платформа 1С:Предприятие автоматически создает индексы для первичных ключей всех таблиц, а также для измерений регистров сведений. Для периодических регистров индексы дополнительно включают поле Период. Мы должны помнить, что порядок измерений в регистре напрямую влияет на состав и эффективность этих автоматически создаваемых индексов в базе данных.
Помимо автоматических, мы можем явно управлять индексированием реквизитов и измерений, устанавливая свойство Индексировать в конфигураторе. Это особенно важно для полей, по которым мы часто выполняем отборы, сортировки или используем их в условиях соединений. Если мы видим, что запрос тормозит по какому-либо полю, которое активно используется в ГДЕ или СОЕДИНЕНИИ, но не является измерением или ключевым реквизитом, нам следует рассмотреть возможность его индексации.
Например, в нашем случае, если запрос медленно работает с полями ИерархияУпаковки и Упаковка, мы должны обязательно проверить наличие индексов по этим полям. Без индекса по Упаковка запрос, скорее всего, не сможет работать эффективно.
Индексирование временных таблиц:
При использовании временных таблиц также рекомендуется их индексировать, если они содержат большой объем данных и активно участвуют в соединениях или используются в подзапросах с оператором В (...). Однако, если временная таблица используется всего 1-2 раза в запросе, индексация может не дать значительного прироста производительности, а иногда даже замедлить процесс из-за накладных расходов на построение индекса.
При создании индексов для временных таблиц мы должны включать те поля, которые используются в условиях ПО для соединений или в списке выбора для оператора В. Это позволит СУБД максимально быстро находить нужные данные.
Переиндексация и обновление статистики:
Иногда проблема может быть связана не с отсутствием индексов, а с их состоянием или устаревшей статистикой базы данных. Мы должны регулярно проводить переиндексацию таблиц и обновлять статистику по ним. Эти операции помогают СУБД строить оптимальные планы выполнения запросов.
Это один из наиболее важных аспектов оптимизации запросов, особенно при работе с виртуальными таблицами, такими как СрезПоследних или Остатки. Способ указания отбора может кардинально повлиять как на производительность, так и на логику получения результата.
Условие в параметрах виртуальной таблицы:
Когда мы указываем условие непосредственно в параметрах виртуальной таблицы (например, РегистрСведений.ИмяРегистра.СрезПоследних(, Измерение = &Значение)), СУБД применяет этот фильтр на этапе построения самой виртуальной таблицы. Это означает, что она сразу отсекает ненужные данные, существенно сокращая объем информации, с которой ей предстоит работать. Такой подход является предпочтительным с точки зрения производительности, так как минимизирует объем обрабатываемых данных.
Посмотрим на пример из форума:
ВЫБРАТЬ
алкХранилищеУпаковокСрезПоследних.Упаковка КАК Упаковка
ИЗ
РегистрСведений.алкХранилищеУпаковок.СрезПоследних(, Упаковка = &Штрихкод) КАК алкХранилищеУпаковокСрезПоследних
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ 1
алкХранилищеУпаковокСрезПоследних.Упаковка
ИЗ
РегистрСведений.алкХранилищеУпаковок.СрезПоследних(, ИерархияУпаковки = &Штрихкод) КАК алкХранилищеУпаковокСрезПоследних
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ 1
алкХранилищеАкцизныхМарокСрезПоследних.Упаковка
ИЗ
РегистрСведений.алкХранилищеАкцизныхМарок.СрезПоследних(, Упаковка = &Штрихкод) КАК алкХранилищеАкцизныхМарокСрезПоследних
В этом запросе условие Упаковка = &Штрихкод (или ИерархияУпаковки = &Штрихкод) передается непосредственно в параметры функции СрезПоследних, что является правильным подходом для оптимизации. Мы сокращаем количество данных, из которых строится срез, с самого начала.
Условие в предложении ГДЕ:
Когда мы указываем условие в предложении ГДЕ (например, ИЗ РегистрСведений.ИмяРегистра.СрезПоследних() КАК СрезПоследних ГДЕ СрезПоследних.Измерение = &Значение), это условие применяется после того, как виртуальная таблица уже полностью построена. В этом случае СУБД сначала тратит ресурсы на построение всего среза (или остатков) по всем измерениям, а затем отфильтровывает из него записи, не удовлетворяющие условию. Это приводит к избыточной работе и значительному снижению производительности.
Рассмотрим пример из форума, где условие ГДЕ применяется к уже построенному срезу:
ВЫБРАТЬ
алкХранилищеУпаковокСрезПоследних.Упаковка КАК Упаковка
ИЗ
РегистрСведений.алкХранилищеУпаковок.СрезПоследних(, ) КАК алкХранилищеУпаковокСрезПоследних
ГДЕ
алкХранилищеУпаковокСрезПоследних.Упаковка = &Штрихкод
Такой подход может быть крайне неэффективным, если регистр содержит много данных, а условие в ГДЕ сильно сокращает выборку. Мы строим большой срез, а потом почти все отбрасываем.
Важное различие в логике для СрезПоследних:
Для виртуальной таблицы СрезПоследних способ указания отбора влияет не только на производительность, но и на логику получения результата. Проанализируем ситуацию на примере:
// Исходные данные регистра сведений:
// Родитель | Состояние | Период
// ----------|-----------|------------
// Папа | Бухой | 01.01.2025
// Папа | Трезвый | 01.03.2025
// Папа | Бухой | 01.05.2025
// Папа | Трезвый | 24.09.2025
a) Запрос с условием в параметрах СрезПоследних по измерению:
ВЫБРАТЬ
СрезПоследних.Состояние
ИЗ
РегистрСведений.МойРегистр.СрезПоследних(, Родитель = &Папа) КАК СрезПоследних
ГДЕ
СрезПоследних.Состояние = &Бухой
В этом случае СрезПоследних сначала найдет последнюю запись по измерению "Папа" (это будет строка "24.09.2025 Папа Трезвый"). Затем к этой единственной записи будет применено условие ГДЕ СрезПоследних.Состояние = &Бухой. Результат: <Нет строк>, так как последняя запись "Трезвый", а не "Бухой".
b) Запрос с условием в параметрах СрезПоследних по ресурсу/реквизиту:
ВЫБРАТЬ
СрезПоследних.Состояние,
СрезПоследних.Период
ИЗ
РегистрСведений.МойРегистр.СрезПоследних(, Состояние = &Бухой) КАК СрезПоследних
Здесь СрезПоследних постарается найти последнюю запись, где Состояние было "Бухой". Результат: "01.05.2025 Папа Бухой".
Это показывает, что отбор по ресурсу или реквизиту в параметрах СрезПоследних может привести к тому, что будет выбрана *последняя* запись по измерениям, даже если значение этого ресурса/реквизита не соответствует условию. В то время как условие в ГДЕ отфильтрует такие записи из уже построенного среза. Мы должны четко понимать эту логику при построении запросов.
"Кривое" проектирование регистров сведений часто является первопричиной медленной работы запросов. Если регистр спроектирован неоптимально, даже самые изощренные методы оптимизации запросов могут не дать желаемого результата.
Анализ структуры и периодичности:
При создании периодических регистров мы должны тщательно анализировать, как данные изменяются во времени. Каждая запись в периодическом регистре должна отражать факт установки значений *всех* ресурсов на определенный момент времени. Если это не так, и записи в регистре не являются "срезами" состояния, то использование виртуальных таблиц СрезПоследних или СрезПервых может быть некорректным или неэффективным.
Избегайте избыточных измерений:
Мы должны избегать создания избыточных измерений, которые не являются ключевыми для уникальности записи или не используются для эффективной фильтрации. Каждое дополнительное измерение увеличивает объем данных и сложность индексов, что может замедлять работу.
Новые режимы записи:
В последних версиях платформы 1С:Предприятие (например, 8.3.25 и выше) планируются улучшения производительности при работе с регистрами сведений, включая новые режимы записи (Слияние, Удаление), которые могут быть полезны для оптимизации и более эффективного управления данными.
Для сложных запросов, включающих несколько условий или источников данных, мы можем использовать операторы ОБЪЕДИНИТЬ ВСЕ и временные таблицы для повышения производительности.
ОБЪЕДИНИТЬ ВСЕ:
В некоторых случаях замена оператора ИЛИ на ОБЪЕДИНИТЬ ВСЕ может ускорить выполнение запроса, особенно если условия ИЛИ применяются к проиндексированным полям. Оператор ОБЪЕДИНИТЬ ВСЕ отличается от ОБЪЕДИНИТЬ тем, что он не выполняет группировку и удаление дублирующихся строк, что может быть значительно быстрее при больших объемах данных, если нам не требуется уникальность строк в результате.
Посмотрим на пример из форума, где используется ОБЪЕДИНИТЬ ВСЕ:
ВЫБРАТЬ
алкХранилищеУпаковокСрезПоследних.Упаковка КАК Упаковка
ИЗ
РегистрСведений.алкХранилищеУпаковок.СрезПоследних(, Упаковка = &Штрихкод) КАК алкХранилищеУпаковокСрезПоследних
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ 1
алкХранилищеУпаковокСрезПоследних.Упаковка
ИЗ
РегистрСведений.алкХранилищеУпаковок.СрезПоследних(, ИерархияУпаковки = &Штрихкод) КАК алкХранилищеУпаковокСрезПоследних
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ 1
алкХранилищеАкцизныхМарокСрезПоследних.Упаковка
ИЗ
РегистрСведений.алкХранилищеАкцизныхМарок.СрезПоследних(, Упаковка = &Штрихкод) КАК алкХранилищеАкцизныхМарокСрезПоследних
Здесь мы объединяем результаты трех отдельных выборок, что может быть быстрее, чем пытаться реализовать все условия в одном блоке ГДЕ с оператором ИЛИ, особенно если источники данных разные или требуют разных виртуальных таблиц.
Временные таблицы:
Использование временных таблиц — это мощный инструмент для повышения производительности и стабильности сложных запросов. Они позволяют разбить сложный запрос на несколько более простых, промежуточные результаты которых сохраняются и затем используются в последующих частях запроса.
Рекомендации по работе с временными таблицами:
СрезПоследних или Остатки) в отдельные запросы с сохранением результатов во временные таблицы. Это позволяет СУБД один раз построить сложный набор данных, а затем многократно использовать его в других частях запроса.Использование составных типов данных, особенно в условиях запросов и при обращении к полям через точку ("разыменование ссылочного поля"), может значительно снижать производительность. Мы должны быть особенно внимательны к типу значения, по которому ставим фильтр, чтобы он не был составным без необходимости.
Проблема составных типов:
При обращении к полю составного типа через точку (например, Ссылка.Реквизит) система неявно создает дополнительные запросы и соединяет таблицы всех возможных типов, входящих в составной тип. Это усложняет SQL-текст, может приводить к неоптимальным планам выполнения запросов и замедлять их.
Индексирование поля составного типа данных также увеличивает нагрузку, поскольку СУБД приходится создавать индексы для каждого отдельного типа, входящего в составной.
Рекомендации по работе с составными типами:
ВЫРАЗИТЬ для явного приведения поля к конкретному типу, если это допустимо по логике запроса. Например, ВЫРАЗИТЬ(МоеПоле КАК Справочник.Номенклатура). Это помогает СУБД точнее определить, с какой таблицей она работает.В для полей составного типа также может вызывать проблемы производительности. В таких случаях мы можем рассмотреть вариант с предварительной выборкой необходимых значений во временную таблицу и последующим внутренним соединением.В ситуациях, когда архитектура базы данных спроектирована крайне неоптимально, и стандартные средства языка запросов 1С не позволяют добиться приемлемой производительности, может потребоваться использование прямых запросов к СУБД (SQL). Однако мы должны рассматривать это как крайнюю меру.
Прямые запросы требуют глубокого понимания работы СУБД, структуры таблиц 1С и платформы в целом. Их использование может усложнить поддержку системы и привести к проблемам при обновлении конфигурации. Мы всегда должны сначала исчерпать все возможности оптимизации на уровне языка запросов 1С, прежде чем прибегать к прямым SQL-запросам.
Надеемся, что эти рекомендации помогут вам оптимизировать медленно работающие запросы в 1С:Предприятии и значительно улучшить производительность ваших решений.
← К списку