Замер времени выполнения временных таблиц больших запросов

13.09.14

Разработка - Инструментарий разработчика

Многим из нас частенько приходится работать с большими и сложными запросами, которые могут включать в себя большое количество временных таблиц. Бывает и такое, что такие запросы сопровождает одновременно несколько человек. А так как количество данных увеличивается, в один прекрасный день, такой запрос начинает выполняться неприемлемо долго.
Прежде чем начать оптимизировать запрос, нам важно понять, в каком месте происходит коллапс. И что именно нам необходимо оптимизировать.
Для того, чтобы это выяснить "вручную", требуется выдержка и много времени. Поэтому, когда мне надоело это все, я решил написать себе автоматический измеритель времени выполнения каждой временной таблицы моего запроса.

Скачать исходный код

Наименование Файл Версия Размер
КонсольЗапросовРасширенная
.epf 41,18Kb
23
.epf 1.0 41,18Kb 23 Скачать
Итак, поскольку все разрабатываемые/поддерживаемые мною запросы я привык хранить в sel файлах, и обкатывать их в консоли, то я не стал заморачиваться с написанием новой обработки. Я просто добавил кнопку и её обработчик в консоль которой привык пользоваться. Поэтому в данной статье постараюсь описать именно суть моей доработки. А так же поделюсь модифицированной версией вполне стандартной консоли запросов.

Идея.

Однажды в очередной раз столкнувшись с тем, что в новой декаде отчет стал работать дольше чем обычно, я подумал что мне нужен инструмент, который бы мог замерить время выполнения каждого подзапроса (временной таблицы) моего большого запроса. Тогда бы я мог точно знать в чем проблема и как её можно решить. Я долго серфил по Интернету в поисках подобного инструмента. Но так ничего и не нашел. Тогда я стал думать как бы я мог это сделать сам.
Все сводилось к тому что мне надо было разбивать запрос на отдельные составляющие, и отдельно их выполнять засекая время выполнения.
Вариантов реализации в голове крутилось несколько. Но лишь путем проб и ошибок я пришел к тому который оказался реальным.

Разбивка запроса.

Для разбивки запроса на мелкие составляющие я использую такой не хитрый алгоритм:
Текст = ЭлементыФормы.ТекстЗапроса.ПолучитьТекст();
	
	МП = Новый Массив;   //Массив подзапросов
	МП.Очистить();
	
	Ш = 0;
	Пока Найти(Текст, ";") > 0 Цикл
		Текст = ОбрезатьНачалоТекста(Текст);
		ПодЗапрос = СокрЛП(Сред(Текст,Найти(Текст,"ВЫБРАТЬ"),Найти(Текст,";") - Найти(Текст,"ВЫБРАТЬ") + 1));
		Если НЕ СокрЛП(ПодЗапрос) = "" Тогда
			МП.Добавить(ПодЗапрос);
		КонецЕсли;
		
		Текст = Сред(Текст, СтрДлина(ПодЗапрос) + 1, СтрДлина(Текст) - СтрДлина(ПодЗапрос) + 1);
		Ш = Ш + 1;
		Если Ш > 1000 Тогда
			Прервать; //предохранитель от зацикливаний
		КонецЕсли;
	КонецЦикла;
Как видно в коде, я помещаю формирование каждой временной таблицы в массив, как отдельную единицу запроса.
Так же перед началом каждой итерации, я вызываю функцию ОбрезатьНачалоТекста.
Я делаю это для того что бы убрать из текста всевозможные комментарии и другие конструкции которые нам не понадобятся при измерении времени.

Вот код этой функции:

Функция ОбрезатьНачалоТекста(Текст)
	
	Пока Найти(Текст, "ВЫБРАТЬ") > 1 Цикл
		Текст = СокрЛ(Сред(Текст, 2));
	КонецЦикла;
	
	Если Найти(Текст, "ВЫБРАТЬ") = 0 Тогда
		Текст = "";
	КонецЕсли;
	
	Возврат Текст;
	
КонецФункции
Дальше для замера времени нам понадобится структура, в которую мы будем записывать время выполнения каждого из подзапросов. И эти данные нам понадобятся для расчета времени выполнения второго подзапроса и всех последующих. То есть, если во втором подзапросе используется временная таблица которая формируется первым делом в нашем запросе, то для начала мы должны выполнить первый подзапрос, а лишь потом выполнить второй. Поэтому мы должны знать сколько времени у нас выполняется первый подзапрос, что бы вычесть это время из общего времени и получить время выполнения именно второго подзапроса.

Создаем такую переменную где будем хранить эти данные. А так же я подготавливаю визуальные компоненты моей консоли запросов для отображения результатов.

ВВП = Новый Структура; //Время выполнения подзапроса
	ВВП.Очистить();
	
	//Подготовим ТЧ для отображения замеров времени
	РезультатТаблица.Очистить();
	РезультатТаблица.Колонки.Очистить();
	ЭтаФорма.ЭлементыФормы.ТаблицаРезультата.Колонки.Очистить();
	РезультатТаблица.Колонки.Добавить("ИмяПодзапроса");
	РезультатТаблица.Колонки.Добавить("Время");
	
	Для Каждого ТекПоле Из РезультатТаблица.Колонки Цикл  //добавим колонки в гриде
		ЭтаФорма.ЭлементыФормы.ТаблицаРезультата.Колонки.Добавить(ТекПоле.Имя);
	КонецЦикла;
	Для Каждого ТекПоле Из ЭтаФорма.ЭлементыФормы.ТаблицаРезультата.Колонки Цикл
		ТекПоле.Данные = ТекПоле.Имя;
	КонецЦикла;
Теперь начинаем непосредственно процесс замера времени.

//Начинаем в цикле замеры времени.
	Для Ш = 0 По МП.Количество() - 1 Цикл
		                                     
		Имя = ПолучитьИмяВременнойТаблицы(МП[Ш]);
		ПЗ = Новый Запрос;
		ПЗ.Текст = "";
		УничтожениеВТ = "";
		
		//На случай если в запросе используется одно имя временной таблицы несколько раз.
		//Перед повторным созданием - удаляем отработавший экземпляр
		Для Ж = 0 По Ш Цикл
			ПризнакИспользованияРанее = Ложь;
			Для К = 0 По Ж Цикл
				Если ПолучитьИмяВременнойТаблицы(МП[Ж]) = ПолучитьИмяВременнойТаблицы(МП[К]) И НЕ К = Ж Тогда
					ПризнакИспользованияРанее = Истина;
				КонецЕсли;
			КонецЦикла;	
			Если ПризнакИспользованияРанее Тогда               
				ПЗ.Текст = ПЗ.Текст + "
				|УНИЧТОЖИТЬ " + ПолучитьИмяВременнойТаблицы(МП[Ж]) + "; 
				|" + МП[Ж];
			Иначе
				ПЗ.Текст = ПЗ.Текст + "
				|" + МП[Ж];
				УничтожениеВТ = УничтожениеВТ + "
				|УНИЧТОЖИТЬ " + ПолучитьИмяВременнойТаблицы(МП[Ж]) + ";";
			КонецЕсли;
		КонецЦикла;
		
		ПЗ.Текст = ПЗ.Текст + УничтожениеВТ;
		
		Для Каждого СтрокаПараметров Из мФормаПараметров.Параметры Цикл
			Если СтрокаПараметров.ЭтоВыражение Тогда
				ПЗ.УстановитьПараметр(СтрокаПараметров.ИмяПараметра, Вычислить(СтрокаПараметров.ЗначениеПараметра));
			Иначе
				ПЗ.УстановитьПараметр(СтрокаПараметров.ИмяПараметра, СтрокаПараметров.ЗначениеПараметра);
			КонецЕсли;
		КонецЦикла;
		
        //Засекаем время
		ВремяНачалаВыполнения = ТекущаяДата();
		Попытка
			ПЗ.Выполнить();
		Исключение
			Сообщить(ОписаниеОшибки());
			Возврат;
		КонецПопытки;
		Затрачено = ТекущаяДата() - ВремяНачалаВыполнения;
		ОбщееВремя = ДатуВЧисло(Дата(Формат('19000101'+Затрачено, "ДФ='dd.MM.yyyy HH:mm:ss'")));
		Предыдущие = 0;
		Для Каждого ТВ Из ВВП Цикл
			Предыдущие = Предыдущие + ТВ.Значение;
		КонецЦикла;
		ТекущееВремя = ОбщееВремя - Предыдущие;
		Если ТекущееВремя < 0 Тогда
			ТекущееВремя = 0;
		КонецЕсли;
		ВВП.Вставить(Имя, ТекущееВремя);
		Сообщить("Талица: " + Имя + " Время: " + Формат(ЧислоВДату(ТекущееВремя), "ДФ='HH:mm:ss'"));
		НС = РезультатТаблица.Добавить();
		НС.ИмяПодзапроса = Имя;
		НС.Время = Формат(ЧислоВДату(ТекущееВремя), "ДФ='HH:mm:ss'");
		
	КонецЦикла;
Как видно в коде, каждую итерацию цикла замера, мы начинаем с получения имени формируемой временной таблицы, а так же с создания нового запроса и его текста. Говоря о тексте, мы как порядочные люди, помимо того что создаем временные таблицы, должны их по завершению и уничтожать, поэтому в конце запроса мы последовательно уничтожаем все что создано. Но бывает так что в разных частях запроса, могут использоваться одинаковые наименования временных таблиц для разных целей. Для этого организуем вложенный цикл для анализа того, какие временные таблицы мы уже создавали ранее и при необходимости зачищаем не нужные таблицы перед созданием новых с таким же именем. 
А для определения имени текущей временной таблицы мы используем такую не хитрую функцию:

Функция ПолучитьИмяВременнойТаблицы(ПодЗапрос)
	
	Старт = Найти(ПодЗапрос, "ПОМЕСТИТЬ") + 9;
	Количество = Найти(ПодЗапрос, "ИЗ") - Старт;
	НаименованиеВТ = СокрЛП(Сред(ПодЗапрос, Старт, Количество));
	
	Возврат НаименованиеВТ; 
	
КонецФункции
После того как с текстом запроса все решено и он готов к исполнению, надо заполнить используемые параметры запроса. Здесь для каждой консоли запросов будут свои нюансы, ну а в моем примере решение для моей консольки. Я стандартным способом заполняю параметры запроса теми значениями что указаны пользователем.

Когда текст запроса готов, и все параметры запроса заполнены, самое время выполнить его и проверить, как долго он выполняется.
Для этого я использую вполне известные приемы для замеров времени. Разве что с временем я оперирую на уровне чисел. То есть храню время выполнения и произвожу с ним математические операции как с числом. Для этого я использую пару простейших функций

//	Функция преобразует получаемую в параметре дату в формат числа (TDouble)
//	Если параметр TDouble установлен в Истина, точка отчета: 30.12.1899 12:00:00 (Традиционно для Delphi)
//	Если параметр TDouble установлен в Ложь (по умолчанию), точка отчета: 01.01.1900 00:00:00 (по умолчанию точка отсчета для 1С)
Функция ДатуВЧисло(Знач пДата, TDouble = Ложь) 
	
	Возврат ?(TDouble, (пДата - Дата(1899,12,30,12,0,0)) / 86400, (пДата - Дата(1900,01,01,0,0,0)) / 86400);
	
КонецФункции

Функция ЧислоВДату(Знач пДата, TDouble = Ложь) 
	
	Возврат Дата(?(TDouble, Дата(1899,12,30,12,0,0) + (пДата * 86400), Дата(1900,01,01,0,0,0) + (пДата * 86400)));
	
КонецФункции

Результаты замера времени я вывожу в ту часть формы консоли куда обычно выводится результат запроса. Но для того что бы отслеживать процесс замера динамически и понимать на каком он этапе, я так же использую вывод замеров через "Сообщить". 

Итак, в результате мы получаем в консоли запросов, дополнительную кнопку, которая не просто выполняет запрос, а делает это столько раз, сколько в запросе временных таблиц. И при этом делает замер времени выполнения каждой из них. Таким образом если выяснится что одна из 40 временных таблиц выполняется за 80% общего времени - вы будете знать где необходимо провести оптимизацию. И действия Ваши будут полны решимости и результата.

p.s.
 Лично я для дебага и замеров делаю отдельную версию запроса. В ней я все результирующие таблицы (не временные) так же помещаю во временные таблицы с условными именами вроде ВТ_ДебагN. Так же если общее время выполнения запроса приближено к 15-20-30 минутам, то почем бы не наложить ограничения "ПЕРВЫЕ NNNNN" в ключевых подзапросах, для экономии времени. Но тут стоит понимать что чем больше данных тем более реальной будет картина замера, и некоторые подзапросы с маленьким количеством данных могут попросту не проявить своих тормозов. Поэтому с этим надо осторожно.

Желаю всем правильных и быстро работающих запросов! :) 

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





запрос время оптимизация замер

См. также

Infostart Toolkit: Инструменты разработчика 1С 8.3 на управляемых формах

Инструментарий разработчика Роли и права Запросы СКД Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    125089    683    389    

732

Infostart PrintWizard - создание и редактирование печатных форм в 1С 8.3

Пакетная печать Печатные формы Инструментарий разработчика Платформа 1С v8.3 Запросы 1С:Зарплата и кадры бюджетного учреждения 1С:Конвертация данных 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 Платные (руб)

Инструмент, позволяющий абсолютно по-новому взглянуть на процесс разработки печатных форм. Благодаря конструктору можно значительно снизить затраты времени на разработку печатных форм, повысить качество и "прозрачность" разработки, а также навести порядок в многообразии корпоративных печатных форм.

18000 руб.

06.10.2023    7800    24    6    

42

Infostart УДиФ: Управление данными и формами 1С

Инструменты администратора БД Инструментарий разработчика Роли и права Платформа 1С v8.3 Конфигурации 1cv8 Россия Платные (руб)

Расширение позволяет без изменения кода конфигурации выполнять проверки при вводе данных, скрывать от пользователя недоступные ему данные, выполнять код в обработчиках. Не изменяет данные конфигурации, легко устанавливается практически на любую конфигурацию на управляемых формах.

10000 руб.

10.11.2023    4301    12    2    

36

SALE! %

PowerTools

Инструментарий разработчика Инструменты администратора БД Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Россия Платные (руб)

Универсальный инструмент программиста для администрирования конфигураций. Сборник наиболее часто используемых обработок под единым интерфейсом.

3600 2280 руб.

14.01.2013    178682    1084    0    

862

Многопоточность. Универсальный «Менеджер потоков» 2.1

Инструментарий разработчика Платформа 1С v8.3 Конфигурации 1cv8 Россия Платные (руб)

Восстановление партий или взаиморасчетов, расчет зарплаты, пакетное формирование документов или отчетов - теперь все это стало доступнее. * Есть желание повысить скорость работы медленных алгоритмов! Но... * Нет времени думать о реализации многопоточности? * о запуске и остановке потоков? * о поддержании потоков в рабочем состоянии? * о передаче данных в потоки и как получить ответ из потока? * об организации последовательности? Тогда ЭТО - то что надо!!!

5000 руб.

07.02.2018    99657    239    97    

298

[ЕХТ] Фреймворк для Расширений 1С

Инструментарий разработчика Платформа 1С v8.3 Управляемые формы Платные (руб)

"Фреймворк для Расширений 1С" это универсальное и многофункциональное решение, упрощающее разработку и поддержку создаваемых Расширений. Поставляется в виде комплекта из нескольких Расширений с открытым исходным кодом. Работает в любых Конфигурациях в режиме Управляемого приложения с режимом совместимости 8.3.12 и выше без необходимости внесения изменений в Конфигурацию.

3000 руб.

27.08.2019    18411    6    8    

40

Выполнение произвольного кода или запроса с параметрами через Web-сервис (замена COM-подключений)

Инструментарий разработчика Обмен между базами 1C Платформа 1С v8.3 Платные (руб)

В процессе работы в 1С часто возникает потребность получить данные из другой базы.  Обычно это делается через COM-соединение, и время выполнения запроса при этом оставляет желать лучшего. В данной публикации представлено универсальное решение, позволяющее практически моментально выполнить произвольный код или запрос с параметрами в другой информационной базе через Web-сервис.

2400 руб.

24.09.2019    23867    16    15    

33

1С HTML Шаблоны / HTML Templates

Инструментарий разработчика Платформа 1С v8.3 Конфигурации 1cv8 Платные (руб)

Быстрая и удобная обработка для работы с шаблонами HTML. Позволяет легко и быстро формировать код HTML.

2040 руб.

27.12.2017    28332    3    10    

16
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. tormozit 7142 13.09.14 09:17 Сейчас в теме
Я долго серфил по Интернету в поисках подобного инструмента. Но так ничего и не нашел.

В подсистеме Инструменты разработчика такая возможность давно есть. Тут описание http://devtool1c.ucoz.ru/index/konsol_zaprosov/0-18 ищи "выполнить все подзапросы" и "Длительность чистая" и еще скриншот http://devtool1c.ucoz.ru/_si/0/50350575.jpg, на котором они видны. А тут http://devtool1c.ucoz.ru/load/master_klass_po_podsisteme_instrumenty_razrabotchika­_2_82/1-1-0-9 есть и описание, как это использовать, для тех кто сам не сумел разобраться.
binex; ixilimuse; +2 Ответить
2. ixilimuse 191 13.09.14 19:24 Сейчас в теме
(1) tormozit, Большущее спасибо за ссылку! :) Как-то так получилось что я мимо прошел, когда искал подобный инструмент. Но зато теперь в курсе! Беглый взгляд говорит о том что вещь в хозяйстве - нужная! :)
6. karakymi4 11 20.04.21 13:25 Сейчас в теме
(1)
, на котором они видны. А тут http://devtool1c.ucoz.ru/load/master_klass_po_podsisteme_instrumenty_razrabotchika­­_2_82/1-1-0-9 есть и описание, как это использовать, для тех кто сам не сумел разобраться.

Ссылка битая
3. ПСВ 114 15.09.14 12:13 Сейчас в теме
Под управляемые формы будет консоль ?
4. ixilimuse 191 15.09.14 11:58 Сейчас в теме
(3) ПСВ, Здравствуйте, позже возможно будет, как только время появится)
Но в статье либо в модуле формы выложенной консоли, вполне универсальный код который можно перенести на любую консоль которой Вы привыкли пользоваться. На УФ максимум надо будет его немного разбить на Клиент/Сервер. Основная часть кода думаю будет на сервере выполняться. И вызываться с помощью команды формы с клиента.
Поэтому если какие-то вопросы будут - с радостью отвечу. ))
5. pmaxm86 14.06.18 10:36 Сейчас в теме
Спасибо, удобно!
ixilimuse; +1 Ответить
Оставьте свое сообщение