Консоль запросов и замер
Знакомимся с консолью запросов — рабочим инструментом отладки, — учимся честно замерять время выполнения и понимаем, зачем нужны индексы и измерения регистра.
Консоль запросов — внешняя обработка, в которой можно писать текст запроса, подставлять параметры, тут же выполнять его и видеть результат в таблице, не трогая код конфигурации.
Пока запрос — три строки, его можно отлаживать «в уме». Но реальный запрос отчёта — это соединения пяти таблиц, вложенные пакеты, десяток параметров. Ковырять такое прямо в модуле, перезапуская всю обработку ради каждой правки, — мучение. Поэтому у каждого 1С-разработчика есть консоль запросов: отдельное окно, где запрос живёт сам по себе. Меняешь текст — жмёшь «Выполнить» — видишь таблицу результата. Это главный инструмент и для отладки логики, и для поиска тормозов.
Что такое консоль запросов и откуда её взять
Консоль — это внешняя обработка (файл .epf), которая открывается в любой типовой конфигурации через «Файл → Открыть». Внутри — поле для текста запроса, таблица параметров и кнопка выполнения. Самые известные: «Консоль запросов» с диска ИТС от фирмы «1С» и открытые консоли сообщества. Все они делают одно и то же — избавляют вас от перекомпиляции ради проверки одной строчки запроса.
По сути консоль внутри выполняет ровно тот код, который вы иначе писали бы руками: создаёт объект Запрос, кладёт в него текст, подставляет параметры и вызывает Выполнить().
// Что делает консоль «под капотом», когда вы жмёте «Выполнить»
Запрос = Новый Запрос;
Запрос.Текст = ТекстИзПоляВвода; // ваш запрос из окна консоли
Запрос.УстановитьПараметр("Дата", ЗначениеПараметраДата); // из таблицы параметров
Результат = Запрос.Выполнить();
ТаблицаНаФорме = Результат.Выгрузить(); // и показывает её в таблице
Как честно замерить время
«Кажется, стало быстрее» — не аргумент. Время меряют числом. В 1С для этого есть функция ТекущаяУниверсальнаяДатаВМиллисекундах(): засекаем момент до выполнения и после, вычитаем — получаем длительность в миллисекундах.
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Склад", Склад);
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Результат = Запрос.Выполнить();
Длительность = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;
Сообщить("Запрос выполнен за " + Длительность + " мс, строк: " + Результат.Количество());
Результат:
Запрос выполнен за 1840 мс, строк: 12760
Две честные привычки при замере. Первое: первый прогон всегда медленнее — СУБД ещё не держит данные в кэше, диск читается «вхолодную». Меряйте два-три раза и смотрите на стабильные цифры, а не на первый выброс. Второе: замеряйте на объёме. На двадцати строках любой запрос «быстрый»; сравнивать «до» и «после» имеет смысл там, где данных много.
Чтобы сравнить две версии запроса, оберните измерение в цикл и усредните — так случайные всплески нагрузки сервера не собьют вас с толку.
СуммаМс = 0;
КоличествоПрогонов = 5;
Для Прогон = 1 По КоличествоПрогонов Цикл
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Запрос.Выполнить();
СуммаМс = СуммаМс + (ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало);
КонецЦикла;
Сообщить("Среднее время: " + (СуммаМс / КоличествоПрогонов) + " мс");
Идея плана запроса
Запрос 1С не выполняется буквально сверху вниз, как написан. Сервер СУБД сначала строит план выполнения — стратегию, как именно достать данные: какие таблицы читать, в каком порядке соединять, использовать индекс или сканировать таблицу целиком. Именно план определяет, будет запрос работать 20 миллисекунд или 20 секунд.
Ключевое различие в плане — как читается таблица:
- Поиск по индексу (index seek) — СУБД сразу прыгает к нужным строкам, как вы находите слово в словаре по алфавиту. Быстро.
- Сканирование таблицы (table scan) — СУБД читает все строки подряд и проверяет каждую. Как читать словарь от корки до корки в поисках одного слова. Медленно, и тем хуже, чем больше таблица.
Реальный план смотрят инструментами сервера СУБД (в PostgreSQL — EXPLAIN ANALYZE, в MS SQL — «Actual Execution Plan»), а поймать проблемные запросы помогает технологический журнал платформы с событием длительных запросов. Это уже уровень эксперта. Но идею — «есть подходящий индекс → seek → быстро; нет → scan → медленно» — держать в голове нужно с первого дня.
Почему измерения регистра — это индексы
Вот где теория плана становится практикой 1С. Когда вы создаёте регистр (накопления или сведений), его измерения — это не просто «поля»: платформа автоматически строит по ним индексы в базе данных. Поэтому отбор по измерению регистра почти всегда идёт по индексу и работает быстро, а отбор по обычному реквизиту — нет.
// Склад и Номенклатура — ИЗМЕРЕНИЯ регистра ТоварыНаСкладах → есть индекс → быстро
ВЫБРАТЬ Остатки.КоличествоОстаток
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад И Номенклатура = &Ном) КАК Остатки
Практический вывод при проектировании: поля, по которым вы часто отбираете, должны быть измерениями регистра, а не ресурсами или реквизитами. А порядок измерений тоже важен — отбор эффективнее всего работает по «левым», первым измерениям (это свойство составного индекса). Ошиблись с составом или порядком измерений — и никакая переписка текста запроса потом не спасёт: индекса, который нужен, просто нет.
Как это работает
Замер в 1С меряет полное время: платформа транслировала запрос в SQL, отправила серверу СУБД, тот построил план, выполнил его и вернул данные, платформа их приняла. Поэтому в цифру входит и сеть, и построение плана, и чтение с диска. Отсюда и «холодный» первый прогон: во второй раз данные уже в кэше СУБД, план построен — время падает. Консоль запросов удобна ровно тем, что весь этот цикл повторяется по одной кнопке, и вы за минуту проверяете пять вариантов запроса вместо пяти перезапусков обработки.
Частые ошибки
- Делать вывод по одному «холодному» прогону. Первый запуск всегда медленный из-за кэша. Меряйте несколько раз и берите стабильное значение.
- Мерить на тестовой базе из десятка записей. Разница между планом с индексом и без него видна только на объёме. Проверяйте близко к рабочим данным.
- Использовать
ТекущаяДата()для замера. Её разрешение — одна секунда, миллисекунды вы не поймаете. Нужна именноТекущаяУниверсальнаяДатаВМиллисекундах(). - Отбирать по реквизиту вместо измерения и ждать скорости. Индекс есть у измерений; по обычному реквизиту СУБД сканирует таблицу.
- Ставить измерением поле «на всякий случай». Каждое измерение — это индекс, который замедляет запись в регистр. Измерения — только то, по чему реально идёт отбор и итоги.
Итоги
- Консоль запросов — внешняя обработка для отладки: пишешь текст, подставляешь параметры, сразу видишь результат без перекомпиляции конфигурации.
- Время меряют функцией
ТекущаяУниверсальнаяДатаВМиллисекундах()«до» и «после»; первый прогон «холодный», меряйте несколько раз и на объёме. - СУБД выполняет запрос по плану: поиск по индексу (seek) — быстро, сканирование таблицы (scan) — медленно.
- Измерения регистра автоматически индексируются, поэтому отбор по измерению идёт по индексу и работает быстро.
- Часто используемые для отбора поля закладывайте измерениями ещё при проектировании — переписать текст запроса задним числом индекс не создаст.