От текста на языке 1С до плана в СУБД: разбор запроса по шагам
Содержание
Запрос 1С не равен SQL-запросу. То, что вы видите в модуле или в Консоли запросов, платформа 1С 8.3 переводит в свой SQL для конкретной СУБД (MS SQL Server, PostgreSQL, Oracle, IBM Db2). Поэтому читать запросы 1С приходится на двух уровнях: текст на языке 1С — в Консоли запросов, а настоящий SQL и план выполнения — через технологический журнал или средства самой СУБД.
Частое заблуждение — искать узкое место только в тексте 1С. Запрос из пяти строк может превратиться в SQL с десятком соединений, если в нём есть обращение через точку к составному полю. Анализ всегда начинается с текста 1С, а заканчивается планом в СУБД.
1. Консоль запросов: где взять и как подготовить
Консоль запросов — внешняя обработка, а не встроенный режим платформы. Её берут из набора универсальных обработок с диска ИТС или используют аналог, встроенный в вашу конфигурацию.
Как открыть внешнюю обработку в интерфейсе «Такси»:
- Запустите базу в режиме
1С:Предприятие. - Откройте главное меню (кнопка с тремя полосками в левом верхнем углу) →
Файл → Открыть. - Выберите файл консоли с расширением
.epf. - Подтвердите предупреждение безопасности, если платформа его покажет.
Для открытия нужно право интерактивно открывать внешние обработки. У обычного пользователя его обычно нет — консоль запускают под администратором или в копии базы.
Консоль выполняет запрос на реальных данных. Тяжёлый запрос без отборов по всей таблице регистра нагрузит сервер СУБД и замедлит работу остальных пользователей. Отлаживайте в копии базы или вне рабочего времени.
Что настроить перед первым запуском
- 📌 Параметры. Каждый
&Параметриз текста нужно заполнить на вкладке параметров. Пустая дата или пустая ссылка дадут пустой или неверный результат. - 📌 Конструктор. Из консоли открывается штатный конструктор запроса. Им удобно собирать соединения и смотреть, какие поля есть у таблицы.
- 📌 Ограничение выборки. Пока запрос не проверен, добавляйте
ПЕРВЫЕ 100, чтобы не тянуть миллионы строк в форму.
Если консоли нет под рукой, текст запроса можно посмотреть в отладчике: поставьте точку останова после присвоения Запрос.Текст, откройте «Вычислить выражение» и посмотрите значение свойства Текст.
2. Как читать текст запроса 1С: порядок секций
Глазами запрос читают не сверху вниз, а в порядке, в котором его исполняет СУБД. Так сразу видно, откуда берутся строки и где они отсекаются.
| Порядок чтения | Секция | Что в ней искать |
|---|---|---|
| 1 | ИЗ | Источники: таблицы, виртуальные таблицы, вложенные запросы |
| 2 | СОЕДИНЕНИЕ ... ПО | Тип соединения и условие связи |
| 3 | ГДЕ | Отборы до группировки |
| 4 | СГРУППИРОВАТЬ ПО | Поля группировки и агрегаты СУММА, КОЛИЧЕСТВО |
| 5 | ИМЕЮЩИЕ | Отборы по агрегатам |
| 6 | ВЫБРАТЬ | Итоговый набор полей и выражений |
| 7 | УПОРЯДОЧИТЬ ПО, ИТОГИ | Сортировка и итоги для отчёта |
Пример, разобранный в таком порядке:
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
СУММА(Продажи.Сумма) КАК Сумма
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
ИМЕЮЩИЕ
СУММА(Продажи.Сумма) > 100000
УПОРЯДОЧИТЬ ПО
Сумма УБЫВ
Читаем: берём движения регистра «Продажи» → оставляем записи за период → сворачиваем по номенклатуре → оставляем позиции с суммой больше 100 000 → сортируем по убыванию.
Что выдаёт тяжёлый запрос ещё при чтении
- 🔍 Точка после составного поля:
Регистратор.Дата,Субконто1.Наименование. Для каждого возможного типа платформа добавит отдельное соединение. - 🔍
ИЛИв условииГДЕпо разным полям. СУБД часто не может применить индекс. - 🔍 Вложенный запрос в условии соединения.
- 🔍
ПОДОБНО "%текст%"с процентом в начале: индекс не используется. - 🔍 Функции над полем в отборе, например
НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) = &День. Лучше сравнивать само поле с диапазоном.
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 | Соединение через хеш-таблицу | Нормально для больших наборов |
Порядок разбора:
- Найдите операцию с наибольшей стоимостью или наибольшим числом обработанных строк.
- Сравните ожидаемое и фактическое число строк. Расхождение в сотни раз — признак устаревшей статистики.
- Найдите таблицу этой операции, переведите её имя в метаданные функцией из раздела 5.
- Вернитесь в текст 1С и найдите, какая конструкция дала эту операцию.
Для PostgreSQL план получают командой EXPLAIN ANALYZE над текстом из журнала. Вместо Index Seek там Index Scan, вместо Table Scan — Seq Scan.
Если Index Scan появляется на запросах, которые раньше работали быстро, сначала проверьте регламентное обслуживание СУБД: обновление статистики и перестроение индексов. Это лечит больше медленных запросов, чем переписывание текста.
7. Приёмы оптимизации запросов 1С
| Проблема в тексте 1С | Что сделать | Риск |
|---|---|---|
Отбор по измерению в ГДЕ у виртуальной таблицы | Перенести в параметр условия | Нет |
| Точка через составной тип | ВЫРАЗИТЬ(Поле КАК Документ.Вид) или выборка из таблицы нужного документа | Строки других типов пропадут из результата — проверьте, что они не нужны |
ИЛИ по разным полям | Два запроса через ОБЪЕДИНИТЬ ВСЕ | Возможны дубли, если условия пересекаются |
| Подзапрос в соединении | Временная таблица с индексом | Нет |
| Запрос в цикле | Один запрос по списку ссылок В (&Список) | Нет |
ПОДОБНО "%..." по большому справочнику | Полнотекстовый поиск или отбор по началу строки | Меняется логика поиска |
Пример: точка через Регистратор
// Медленно: соединение со всеми видами документов-регистраторов
ВЫБРАТЬ Продажи.Регистратор.Дата КАК Дата
ИЗ РегистрНакопления.Продажи КАК Продажи
// Быстрее: соединение только с одним видом документа
ВЫБРАТЬ ВЫРАЗИТЬ(Продажи.Регистратор КАК Документ.РеализацияТоваровУслуг).Дата КАК Дата
ИЗ РегистрНакопления.Продажи КАК Продажи
ГДЕ Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
Если у регистра 20 видов регистраторов, первый вариант даст 20 соединений в SQL. Второй — одно. Часто дата нужна вовсе не документа, а движения: тогда достаточно поля Продажи.Период, и соединение не нужно совсем.
Когда переписывать запрос не нужно
- 🟢 Запрос выполняется раз в сутки в регламентном задании и укладывается в отведённое время.
- 🟢 Узкое место — блокировки, а не чтение. В журнале это видно по ожиданию на событиях
TLOCKиTTIMEOUT, а не по длительностиDBMSSQL. - 🟢 Медленно стало у всех и сразу после роста базы: начните с обслуживания СУБД и ресурсов сервера.