От текста на языке 1С до плана в СУБД: разбор запроса по шагам

· 10 мин

Содержание

Запрос 1С не равен SQL-запросу. То, что вы видите в модуле или в Консоли запросов, платформа 1С 8.3 переводит в свой SQL для конкретной СУБД (MS SQL Server, PostgreSQL, Oracle, IBM Db2). Поэтому читать запросы 1С приходится на двух уровнях: текст на языке 1С — в Консоли запросов, а настоящий SQL и план выполнения — через технологический журнал или средства самой СУБД.

Частое заблуждение — искать узкое место только в тексте 1С. Запрос из пяти строк может превратиться в SQL с десятком соединений, если в нём есть обращение через точку к составному полю. Анализ всегда начинается с текста 1С, а заканчивается планом в СУБД.

1. Консоль запросов: где взять и как подготовить

Консоль запросов — внешняя обработка, а не встроенный режим платформы. Её берут из набора универсальных обработок с диска ИТС или используют аналог, встроенный в вашу конфигурацию.

Как открыть внешнюю обработку в интерфейсе «Такси»:

  1. Запустите базу в режиме 1С:Предприятие.
  2. Откройте главное меню (кнопка с тремя полосками в левом верхнем углу) → Файл → Открыть.
  3. Выберите файл консоли с расширением .epf.
  4. Подтвердите предупреждение безопасности, если платформа его покажет.

Для открытия нужно право интерактивно открывать внешние обработки. У обычного пользователя его обычно нет — консоль запускают под администратором или в копии базы.

Консоль выполняет запрос на реальных данных. Тяжёлый запрос без отборов по всей таблице регистра нагрузит сервер СУБД и замедлит работу остальных пользователей. Отлаживайте в копии базы или вне рабочего времени.

Что настроить перед первым запуском

  • 📌 Параметры. Каждый &Параметр из текста нужно заполнить на вкладке параметров. Пустая дата или пустая ссылка дадут пустой или неверный результат.
  • 📌 Конструктор. Из консоли открывается штатный конструктор запроса. Им удобно собирать соединения и смотреть, какие поля есть у таблицы.
  • 📌 Ограничение выборки. Пока запрос не проверен, добавляйте ПЕРВЫЕ 100, чтобы не тянуть миллионы строк в форму.

Если консоли нет под рукой, текст запроса можно посмотреть в отладчике: поставьте точку останова после присвоения Запрос.Текст, откройте «Вычислить выражение» и посмотрите значение свойства Текст.

2. Как читать текст запроса 1С: порядок секций

Глазами запрос читают не сверху вниз, а в порядке, в котором его исполняет СУБД. Так сразу видно, откуда берутся строки и где они отсекаются.

Порядок чтенияСекцияЧто в ней искать
1ИЗИсточники: таблицы, виртуальные таблицы, вложенные запросы
2СОЕДИНЕНИЕ ... ПОТип соединения и условие связи
3ГДЕОтборы до группировки
4СГРУППИРОВАТЬ ПОПоля группировки и агрегаты СУММА, КОЛИЧЕСТВО
5ИМЕЮЩИЕОтборы по агрегатам
6ВЫБРАТЬИтоговый набор полей и выражений
7УПОРЯДОЧИТЬ ПО, ИТОГИСортировка и итоги для отчёта

Пример, разобранный в таком порядке:

ВЫБРАТЬ
    Продажи.Номенклатура КАК Номенклатура,
    СУММА(Продажи.Сумма) КАК Сумма
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура
ИМЕЮЩИЕ
    СУММА(Продажи.Сумма) > 100000
УПОРЯДОЧИТЬ ПО
    Сумма УБЫВ

Читаем: берём движения регистра «Продажи» → оставляем записи за период → сворачиваем по номенклатуре → оставляем позиции с суммой больше 100 000 → сортируем по убыванию.

Что выдаёт тяжёлый запрос ещё при чтении

  • 🔍 Точка после составного поля: Регистратор.Дата, Субконто1.Наименование. Для каждого возможного типа платформа добавит отдельное соединение.
  • 🔍 ИЛИ в условии ГДЕ по разным полям. СУБД часто не может применить индекс.
  • 🔍 Вложенный запрос в условии соединения.
  • 🔍 ПОДОБНО "%текст%" с процентом в начале: индекс не используется.
  • 🔍 Функции над полем в отборе, например НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) = &День. Лучше сравнивать само поле с диапазоном.

3. Пакетный запрос и временные таблицы

Пакет — несколько запросов в одном тексте, разделённых точкой с запятой. Промежуточные результаты складывают во временные таблицы через ПОМЕСТИТЬ.

ВЫБРАТЬ
    Товары.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_Товары
ИЗ
    Справочник.Номенклатура КАК Товары
ГДЕ
    Товары.Родитель В ИЕРАРХИИ (&Группа)
ИНДЕКСИРОВАТЬ ПО
    Номенклатура
;
////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Остатки.Номенклатура,
    Остатки.КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(
        &Дата,
        Номенклатура В (ВЫБРАТЬ ВТ.Номенклатура ИЗ ВТ_Товары КАК ВТ)
    ) КАК Остатки

Как читать пакет:

  1. Найдите все ПОМЕСТИТЬ — это промежуточные шаги.
  2. Для каждой временной таблицы найдите, где она используется дальше по тексту.
  3. Последний запрос без ПОМЕСТИТЬ обычно и есть результат, который видит пользователь или отчёт.

В Консоли запросов результат каждого запроса пакета можно посмотреть отдельно. Это главный приём отладки: вы видите, на каком шаге пропали или размножились строки.

Когда временная таблица ускоряет, а когда нет

  • ✅ Помогает, если один и тот же набор используется в нескольких запросах пакета.
  • ✅ Помогает вместо вложенного запроса в соединении: оптимизатор СУБД лучше работает с готовой таблицей, чем с подзапросом.
  • ✅ ИНДЕКСИРОВАТЬ ПО имеет смысл, когда таблица большая и по полю дальше идёт соединение или отбор.
  • ❌ Индекс на таблице из десятка строк — лишняя работа: его построение дороже выигрыша.

Если временные таблицы нужны в нескольких запросах кода, используют МенеджерВременныхТаблиц. В отладчике у менеджера есть коллекция Таблицы — через неё видно содержимое уже созданных временных таблиц.

4. Виртуальные таблицы регистров и их параметры

Виртуальные таблицы — главный источник ошибок в запросах к регистрам. Это не физические таблицы, а заготовки, которые платформа разворачивает в SQL с учётом таблиц итогов.

Виртуальная таблицаРегистрПараметры
ОстаткиНакопления (остатки), бухгалтерииПериод, условие
ОборотыНакопления, бухгалтерииНачало, конец, периодичность, условие
ОстаткиИОборотыНакопления (остатки), бухгалтерииНачало, конец, периодичность, метод дополнения, условие
СрезПоследнихСведений, периодическийПериод, условие
СрезПервыхСведений, периодическийПериод, условие

Условие в параметрах, а не в ГДЕ

Сравните два варианта:

// Медленно: остатки считаются по всем товарам, потом фильтруются
ВЫБРАТЬ Остатки.Номенклатура, Остатки.КоличествоОстаток
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Остатки
ГДЕ Остатки.Склад = &Склад

// Быстро: отбор попадает внутрь расчёта остатков
ВЫБРАТЬ Остатки.Номенклатура, Остатки.КоличествоОстаток
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Остатки

Отбор по измерениям регистра пишите в параметре условия виртуальной таблицы, а не в ГДЕ. Иначе платформа сначала соберёт остатки по всей базе, а потом отбросит лишнее.

Чего не делать в параметре условия:

  • ⛔ Ссылаться на поля других таблиц запроса. Там допустимы только поля регистра, параметры и вложенные запросы.
  • ⛔ Ставить дату внутри дня без учёта границы. Остатки на &Дата — это остатки на начало этого момента; для остатка на конец дня передавайте Новый Граница(КонецДня(Дата), ВидГраницы.Включая).

Почему остатки на текущую дату считаются быстрее

Остатки без указания периода или на дату позже последней рассчитанной платформа берёт из таблицы итогов. Остатки на дату в прошлом — это итоги плюс-минус движения за разницу. Чем дальше дата от периода рассчитанных итогов, тем больше движений придётся перебрать.

5. Как увидеть настоящий SQL-запрос 1С

Текст, который ушёл в СУБД, собирают технологическим журналом. Настройка — файл logcfg.xml в каталоге conf платформы, например C:\Program Files\1cv8\conf\ на сервере 1С.

Минимальная настройка для MS SQL Server:

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
  <log location="D:\techlog" history="2">
    <event>
      <eq property="name" value="DBMSSQL"/>
      <eq property="p:processName" value="ИмяБазы"/>
    </event>
    <property name="all"/>
  </log>
  <plansql/>
</config>
  • 📁 location — папка для логов, лучше на отдельном диске.
  • ⏳ history — сколько часов хранить файлы.
  • 🗄 DBMSSQL — событие запросов к MS SQL Server. Для PostgreSQL событие называется DBPOSTGRS.
  • 🎯 p:processName — имя информационной базы, чтобы не собирать запросы всех баз кластера.
  • 📊 <plansql/> — добавляет план выполнения в свойство planSQLText (для MS SQL Server).

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

Журнал со свойством all и планами растёт на гигабайты за минуты на нагруженной базе. Включайте его на короткое время, добавляйте фильтр по длительности события и удаляйте logcfg.xml сразу после сбора данных.

Как читать запись журнала

В событии ищите три свойства:

  • Sql — текст SQL-запроса с параметрами.
  • Context — строка модуля 1С, откуда пришёл запрос. По ней находят исходный текст запроса 1С.
  • planSQLText — план выполнения.

Сам SQL выглядит непривычно: вместо имён объектов — служебные таблицы и поля.

SELECT T1._IDRRef, T1._Description
FROM dbo._Reference42 T1
WHERE (T1._Fld1534RRef = @P1)

Как сопоставить _Reference42 и _Fld1534 с метаданными

Соответствие имён возвращает функция ПолучитьСтруктуруХраненияБазыДанных(). Её можно выполнить на сервере во внешней обработке или в консоли кода:

Структура = ПолучитьСтруктуруХраненияБазыДанных(, Истина);
Для Каждого СтрокаТаблицы Из Структура Цикл
    Если СтрокаТаблицы.ИмяТаблицыХранения = "_Reference42" Тогда
        Сообщить(СтрокаТаблицы.ИмяТаблицы);
        Для Каждого Поле Из СтрокаТаблицы.Поля Цикл
            Сообщить(Поле.ИмяПоляХранения + " = " + Поле.ИмяПоля);
        КонецЦикла;
    КонецЕсли;
КонецЦикла;

Второй параметр Истина возвращает имена в том виде, в каком они записаны в СУБД. Для PostgreSQL имена будут в нижнем регистре: _reference42.

Основные префиксы, которые встретятся в SQL:

ПрефиксЧто это
_ReferenceСправочник
_DocumentДокумент
_AccumRgДвижения регистра накопления
_AccumRgTИтоги регистра накопления
_InfoRgРегистр сведений
_AccRgДвижения регистра бухгалтерии
_FldРеквизит или измерение
#ttВременная таблица запроса

6. План выполнения: как найти узкое место

План показывает, как СУБД реально достаёт данные. Читают его от самых вложенных операций к верхней.

Операция в плане MS SQLЧто значитХорошо или плохо
Index SeekПоиск по индексуХорошо
Index Scan / Clustered Index ScanЧтение индекса целикомПлохо на больших таблицах
Table ScanЧтение таблицы без индексаПлохо, кроме маленьких временных таблиц
Key LookupДочитывание полей, которых нет в индексеТерпимо при малом числе строк
Nested LoopsВложенный цикл соединенияХорошо для малых наборов
Hash MatchСоединение через хеш-таблицуНормально для больших наборов

Порядок разбора:

  1. Найдите операцию с наибольшей стоимостью или наибольшим числом обработанных строк.
  2. Сравните ожидаемое и фактическое число строк. Расхождение в сотни раз — признак устаревшей статистики.
  3. Найдите таблицу этой операции, переведите её имя в метаданные функцией из раздела 5.
  4. Вернитесь в текст 1С и найдите, какая конструкция дала эту операцию.

Для PostgreSQL план получают командой EXPLAIN ANALYZE над текстом из журнала. Вместо Index Seek там Index Scan, вместо Table Scan — Seq Scan.

Если Index Scan появляется на запросах, которые раньше работали быстро, сначала проверьте регламентное обслуживание СУБД: обновление статистики и перестроение индексов. Это лечит больше медленных запросов, чем переписывание текста.

Диагностика: выберите симптом

7. Приёмы оптимизации запросов 1С

Проблема в тексте 1СЧто сделатьРиск
Отбор по измерению в ГДЕ у виртуальной таблицыПеренести в параметр условияНет
Точка через составной типВЫРАЗИТЬ(Поле КАК Документ.Вид) или выборка из таблицы нужного документаСтроки других типов пропадут из результата — проверьте, что они не нужны
ИЛИ по разным полямДва запроса через ОБЪЕДИНИТЬ ВСЕВозможны дубли, если условия пересекаются
Подзапрос в соединенииВременная таблица с индексомНет
Запрос в циклеОдин запрос по списку ссылок В (&Список)Нет
ПОДОБНО "%..." по большому справочникуПолнотекстовый поиск или отбор по началу строкиМеняется логика поиска

Пример: точка через Регистратор

// Медленно: соединение со всеми видами документов-регистраторов
ВЫБРАТЬ Продажи.Регистратор.Дата КАК Дата
ИЗ РегистрНакопления.Продажи КАК Продажи

// Быстрее: соединение только с одним видом документа
ВЫБРАТЬ ВЫРАЗИТЬ(Продажи.Регистратор КАК Документ.РеализацияТоваровУслуг).Дата КАК Дата
ИЗ РегистрНакопления.Продажи КАК Продажи
ГДЕ Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг

Если у регистра 20 видов регистраторов, первый вариант даст 20 соединений в SQL. Второй — одно. Часто дата нужна вовсе не документа, а движения: тогда достаточно поля Продажи.Период, и соединение не нужно совсем.

Когда переписывать запрос не нужно

  • 🟢 Запрос выполняется раз в сутки в регламентном задании и укладывается в отведённое время.
  • 🟢 Узкое место — блокировки, а не чтение. В журнале это видно по ожиданию на событиях TLOCK и TTIMEOUT, а не по длительности DBMSSQL.
  • 🟢 Медленно стало у всех и сразу после роста базы: начните с обслуживания СУБД и ресурсов сервера.

Частые вопросы

Можно ли выполнить SQL-запрос напрямую из 1С?
Встроенными средствами — нет: объект Запрос понимает только язык запросов 1С. Прямые запросы к СУБД через внешние соединения ADO обходят права и блокировки платформы, для изменения данных их применять нельзя.
Чем язык запросов 1С отличается от SQL?
Он только читает данные: нет INSERT, UPDATE и DELETE. Зато есть обращение через точку, виртуальные таблицы регистров, ИТОГИ и В ИЕРАРХИИ, которых в чистом SQL нет.
Почему в Консоли запросов быстро, а у пользователя медленно?
Чаще всего из-за ограничений доступа на уровне записей: под пользователем с RLS платформа дописывает к SQL дополнительные условия. Проверьте запрос под этим пользователем и сравните SQL в техжурнале.
Работает ли техжурнал в файловой базе?
Да, logcfg.xml действует и для файлового варианта. Но событий DBMSSQL там не будет: файловая база работает со своим движком, и плана выполнения SQL у неё нет.
Влияет ли включённый техжурнал на скорость работы?
Влияет. Сбор всех свойств и планов заметно нагружает диск и процессор сервера 1С, поэтому журнал включают на время диагностики с фильтром по базе и длительности.