Знакомая картина: в технологическом журнале висит запрос на несколько экранов, время выполнения неприличное, а понять, что он делает, нельзя. Потому что написано там примерно так:
SELECT TOP 1000 T1._Fld40219RRef, T1._Fld40222RRef, SUM(T1._Fld40225) FROM _AccumRgT40229 T1 INNER JOIN _Reference296 T2 ON T1._Fld40219RRef = T2._IDRRef WHERE T1._Period = @P1 AND T1._Fld1809 = @P2 GROUP BY T1._Fld40219RRef, T1._Fld40222RRef
Плана выполнения тут мало. Прежде чем думать про индексы, надо ответить на вопрос попроще: какой отчёт или какая обработка это выполнила и по каким данным. То есть перевести текст обратно в термины конфигурации.
Дальше разбор: почему ручной перевод дороже, чем кажется, что именно отдаёт платформа, и три места, где расшифровка обрывается совсем.
Оговорюсь сразу про честность примера. Все имена таблиц и полей ниже настоящие, я снял их с тестовой УТ 11.5.22.67 штатным вызовом платформы, и они воспроизводятся у любого на своей базе. А вот сам текст запроса выше я собрал как типичный: живую трассировку с чужого прода в статью тащить нельзя.
Сколько таблиц стоит за одним объектом
Первое, обо что спотыкается ручной разбор, - количество. Я попросил у платформы структуру хранения для четырёх объектов: двух справочников, одного документа и одного регистра накопления.
Вернулось двадцать пять таблиц.
Вот как они распределились:
| Имя в СУБД | Что это в конфигурации |
|---|---|
_Reference296 |
Справочник.Номенклатура |
_Reference296_VT6460 |
её табличная часть ДополнительныеРеквизиты |
_Reference296_VT6465 |
её табличная часть ДрагоценныеМатериалы |
_Reference296_VT6472 |
её табличная часть Представления |
_ReferenceChngR6476 |
регистрация изменений по номенклатуре |
_Reference335 |
Справочник.Партнеры |
_Document741 |
Документ.РеализацияТоваровУслуг |
_Document741_VT22851 |
его табличная часть Товары |
_AccumRg40218 |
РегистрНакопления.ТоварыНаСкладах |
_AccumRgT40229 |
таблица итогов этого регистра |
И это ещё сокращённый список: у одной только "Реализации товаров и услуг" девять табличных частей, то есть девять отдельных таблиц плюс основная плюс регистрация изменений. Одиннадцать физических таблиц на один документ.
Практический вывод для разбора: если в запросе десяток таблиц, это не обязательно десяток объектов конфигурации. Вполне может быть два документа со своими табличными частями. И наоборот, один невинный _Document741 в тексте не означает, что запрос трогает только шапку.
Префикс говорит, что перед вами, ещё до расшифровки
Пока не полез в структуру хранения, кое-что можно понять по самому имени. В том замере встретились такие префиксы:
| Префикс | Что это |
|---|---|
_Reference |
справочник |
_Document |
документ |
_AccumRg |
регистр накопления, движения |
_AccumRgT |
он же, таблица итогов |
_AccumRgOpt |
настройки хранения его итогов |
_ReferenceChngR, _DocumentChngR, _AccumRgChngR |
регистрация изменений для обмена |
_RefSInf |
служебная таблица предопределённых элементов |
суффикс _VTчисло |
табличная часть родительской таблицы |
Список неполный: это ровно то, что вернулось на четырёх объектах, регистров сведений и планов видов характеристик среди них не было.
Пользы от префикса три копейки, но иногда они решают. Увидели в верхушке трассировки _AccumRgT - запрос идёт по итогам, и первый вопрос тут про сами итоги: на какую дату они рассчитаны и не отключены ли вовсе. Индексы подождут. Увидели _VT без основной таблицы рядом - кто-то читает табличную часть отдельно от шапки.
Что платформа отдаёт сама
Расшифровка живёт в одном методе:
Список = Новый Массив; Список.Добавить("Справочник.Номенклатура"); Список.Добавить("Документ.РеализацияТоваровУслуг"); Список.Добавить("РегистрНакопления.ТоварыНаСкладах"); Структура = ПолучитьСтруктуруХраненияБазыДанных(Список, Истина); Для Каждого Строка Из Структура Цикл Сообщить(Строка.ИмяТаблицыХранения + " = " + Строка.ИмяТаблицы + " [" + Строка.Назначение + "]"); КонецЦикла;
Второй параметр - это и есть ключ: он говорит, что имена нужны в терминах базы данных. Без него метод отдаёт другую сторону.
Пустой первый параметр вернёт всю конфигурацию целиком. На УТ это несколько тысяч строк, и каждая строка тащит за собой вложенные таблицы значений с полями и индексами. Если задача - разобрать один запрос, список объектов лучше передавать явно: разница по времени видна невооружённым глазом.
Внутри каждой строки лежит коллекция Поля, и в ней та же пара: ИмяПоляХранения и ИмяПоля. Для нашего регистра накопления она выглядит так:
| Поле в СУБД | Реквизит |
|---|---|
_Period |
Период |
_RecorderTRef |
Регистратор, тип значения |
_RecorderRRef |
Регистратор, ссылка |
_LineNo |
НомерСтроки |
_Active |
Активность |
_RecordKind |
ВидДвижения |
_Fld40219RRef |
Номенклатура |
_Fld40220RRef |
Характеристика |
_Fld40222RRef |
Склад |
_Fld40225 |
ВНаличии |
_Fld40226 |
КОтгрузке |
_Fld1809 |
ОбластьДанныхОсновныеДанные |
Теперь запрос из начала статьи читается: остатки по номенклатуре и складу, поле _Fld40225 - это "В наличии", а _Fld1809 в условии - разделитель данных, который платформа подставляет сама.
Обратите внимание на регистратор. Один реквизит занимает два поля: _RecorderTRef хранит тип, _RecorderRRef - ссылку. Так платформа хранит любое поле составного типа. Поэтому счёт "полей в запросе больше, чем реквизитов в конфигурации" сходится не всегда, и это не признак того, что вы смотрите не туда.
Три места, где расшифровка обрывается
Вот тут начинается интересное, и именно из-за этого ручной разбор по дереву метаданных заходит в тупик.
Первое: у таблицы итогов имён полей нет
Регистр накопления с включёнными итогами хранится в двух таблицах: движения в _AccumRg40218 и рассчитанные итоги в _AccumRgT40229. Поля в них одинаковые - те же _Fld40219RRef и _Fld40225.
Так вот: для таблицы итогов платформа возвращает пустое имя объекта и пустые имена всех полей. Метод честно отдаёт строку с назначением "Итоги", а колонка ИмяТаблицы в ней пуста. Внутри, в коллекции полей, ИмяПоляХранения заполнено, ИмяПоле - пустая строка у каждого.
Практически это значит вот что. Медленный запрос к остаткам почти всегда идёт по таблице итогов, а не по движениям: платформа для этого их и держит. То есть самый частый случай в трассировке - именно тот, где штатная расшифровка молчит.
Выкручиваются так: поле _Fld40219RRef ищется в основной таблице того же регистра, а её имя получается отбрасыванием буквы T из _AccumRgT40229. Номер один и тот же, значит и реквизит один и тот же. Правило рабочее, но его надо знать, и в дереве метаданных его никто не покажет.
Второе: таблицы, у которых объекта в конфигурации нет вовсе
В тех же двадцати пяти строках попались три штуки, у которых имя в терминах конфигурации пустое, и не потому, что платформа поленилась, а потому, что называть нечего:
_AccumRgOpt40468- настройки хранения итогов регистра;_RefSInf7273- инициализированные предопределённые данные справочника;_ReferenceChngR6476и_DocumentChngR22969- регистрация изменений для обмена.
Последние две ищутся в конфигурации хотя бы по имени объекта, а первые две не ищутся никак. Если такая таблица вылезла в верхушке трассировки, никакой поиск по дереву метаданных не поможет: там этого объекта нет.
Отдельно про регистрацию изменений. Увидели ChngR в горячем запросе - это не отчёт тормозит, это обмен данными. Диагноз меняется целиком: смотреть надо состав узлов и объём очереди регистрации, индексы тут ни при чём.
Третье: номер поля живёт своей жизнью
Номера в именах вида _Fld40219 сквозные по всей конфигурации и выдаются по порядку создания. Отсюда два следствия, которые ловят на разборе.
Первое: у измерений регистра номера идут подряд - 40219, 40220, 40221, 40222, 40223, 40224, - а дальше ресурсы 40225 и 40226. Порядок совпадает с порядком в конфигураторе, и по нему легко угадывать. Угадывание работает ровно до первого удалённого реквизита: номер выбывает навсегда и больше никому не достаётся, при этом соседи остаются на своих местах.
Второе: одно и то же поле _Fld1809 встретилось и в справочнике, и в регистре. Это разделитель данных, у него один номер на всю конфигурацию. То есть по номеру нельзя судить, какому объекту поле принадлежит, - только в паре с таблицей.
Как это выглядит в работе
Когда таких запросов один-два, всё вышеописанное делается руками: выгрузил структуру, нашёл номера, подставил. Полчаса на запрос.
Когда их тридцать из ночной трассировки, руками уже не выходит. Для этого я и собрал Трансформатор SQL в 1С: на вход подаётся текст запроса целиком, на выходе тот же текст, где имена таблиц и полей заменены на объекты конфигурации. Он держит и временные таблицы, и параметры, и составные поля, и как раз те случаи с итогами, о которых выше.
Важно, чего он не делает: к таблицам базы данных обработка не обращается вообще. Она работает с текстом и со структурой хранения, которую отдаёт сама платформа. Права на СУБД для этого не нужны, и на боевой базе она ничего не читает.
Границы применимости
Скажу прямо, чего в этом разборе нет.
Замера времени не приводится. Я не мерил, сколько занимает расшифровка на конфигурации размером с ERP: замер снят на УТ и на четырёх объектах. Полная выгрузка структуры на большой конфигурации заметно тяжелее, и это стоит держать в голове.
Правило про таблицу итогов проверено на регистре накопления. Регистры сведений с итогами и регистры бухгалтерии я так не проверял, и утверждать, что там всё устроено идентично, не буду.
Имена сняты на платформе 8.3.27. Схема именования держится много лет и вряд ли изменится, но это наблюдение, а не гарантия совместимости.
Разбор не отвечает на вопрос "кто выполнил запрос". Он отвечает на вопрос "по каким данным". Связать запрос с конкретным отчётом или обработкой - отдельная работа по технологическому журналу, и её эта расшифровка не заменяет.
Открытый вопрос
Меня давно занимает вот что. Платформа знает соответствие полей для основной таблицы регистра и не отдаёт его для таблицы итогов, хотя поля там ровно те же и номера совпадают. Похоже на техническое ограничение: смысла прятать расшифровку итогов я не вижу.
Интересно, кто как выкручивается на разборе трассировки: доклеиваете расшифровку по основной таблице, как описано выше, держите собственный справочник соответствий или вообще читаете _Fld по номерам, привыкнув к ним настолько, что перевод уже не нужен?
Другие наши инструменты:
- Трансформатор SQL в 1С - то, о чём статья: текст запроса из профайлера на входе, имена справочников и регистров на выходе.
- УНИЧТОЖИТЬ в конце пакета - следующий шаг, когда запрос уже прочитан: разбирает пакет и показывает, где временная таблица живёт дольше, чем нужна.
- Чек-ап СУБД под 1С - вторая половина диагностики. Запрос бывает нормальным, а тормозит всё равно, и тогда вопросы к настройкам сервера.
- Карта объёмов базы 1С - что именно занимает место, если в трассировке верхушку держит одна и та же таблица.
- Анализ нагрузки кластера 1С - кто блокировал базу и кто грузил сервер, по данным RAS.
Вступайте в нашу телеграмм-группу Инфостарт