Автоматическая подстановка уничтожений временных таблиц в текст запроса

Функция, которая сама подставляет в текст запроса "УНИЧТОЖИТЬ" в местах последнего использования ВТ.

Пока все рассуждают на тему, «Нужно ли удалять временные таблицы», мне это периодически делать приходится. Посему написал функцию, которая делает это сама. Выкладываю здесь — быть может, кому пригодится. 

Механизм реализован при помощи относительно нового объекта «СхемаЗапроса». Поэтому работать будет только под платформой начиная с 8.3.5.1068.

Пока что во всех ситуациях ошибок не было, но если появятся, то прошу оставлять примеры в комментариях.

В публикации есть полный текст механизма + обработка для «поглазеть». В ней ничего особенного, просто тот же механизм, завернутый в обработку с двумя кнопками «Подставить уничтожения» (сам механизм) и «Сравнить» (сравнение текста результата с оригинальным текстом).   

Функция ТекстЗапросаСУничтожениямиВременныхТаблиц(ТекстЗапроса) Экспорт

СхемаЗапроса    = Новый СхемаЗапроса;
СхемаЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);

ТаблицаЗапроса    = Новый ТаблицаЗначений;
ТаблицаЗапроса.Колонки.Добавить("ИндексПакетаЗапроса", Новый ОписаниеТипов("Число"));
ТаблицаЗапроса.Колонки.Добавить("ИспользуемаяВременнаяТаблица", Новый ОписаниеТипов("Строка",,Новый КвалификаторыСтроки(150)));

Для Каждого ПакетЗапроса Из СхемаЗапроса.ПакетЗапросов Цикл

#Если Клиент Тогда
Состояние("Обработка пакета " + ПакетЗапроса.Представление());
#КонецЕсли

ОбработатьПакет(СхемаЗапроса, ПакетЗапроса, ТаблицаЗапроса);

КонецЦикла;

ТаблицаЗапроса.Свернуть("ИндексПакетаЗапроса,ИспользуемаяВременнаяТаблица");

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|    ТаблицаЗапроса.ИндексПакетаЗапроса,
|    ТаблицаЗапроса.ИспользуемаяВременнаяТаблица
|ПОМЕСТИТЬ ВТ_ТаблицаЗапроса
|ИЗ
|    &ТаблицаЗапроса КАК ТаблицаЗапроса
|;
|
////////////////////////////////////////////////////////////////////////////////

|ВЫБРАТЬ
|    ВТ_ТаблицаЗапроса.ИспользуемаяВременнаяТаблица КАК ИмяТаблицы,
|    МАКСИМУМ(ВТ_ТаблицаЗапроса.ИндексПакетаЗапроса) + 1 КАК ИндексДляПакетаУдаления
|ИЗ
|    ВТ_ТаблицаЗапроса КАК ВТ_ТаблицаЗапроса
|
|СГРУППИРОВАТЬ ПО
|    ВТ_ТаблицаЗапроса.ИспользуемаяВременнаяТаблица
|
|УПОРЯДОЧИТЬ ПО
|    ИндексДляПакетаУдаления УБЫВ";
Запрос.УстановитьПараметр("ТаблицаЗапроса", ТаблицаЗапроса);
ТаблицаПоследнегоИспользования    = Запрос.Выполнить().Выгрузить();

СхемаНовогоЗапроса    = Новый СхемаЗапроса;
СхемаНовогоЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);

Для Каждого стрТаблицаПоследнегоИспользования Из ТаблицаПоследнегоИспользования Цикл

ПакетУничтожения    = СхемаНовогоЗапроса.ПакетЗапросов.Добавить(Тип("ЗапросУничтоженияТаблицыСхемыЗапроса"));
ПакетУничтожения.ИмяТаблицы = стрТаблицаПоследнегоИспользования.ИмяТаблицы;

СтарыйИндекс    = СхемаНовогоЗапроса.ПакетЗапросов.Индекс(ПакетУничтожения);
НовыйИндекс     = стрТаблицаПоследнегоИспользования.ИндексДляПакетаУдаления;

СхемаНовогоЗапроса.ПакетЗапросов.Сдвинуть(СтарыйИндекс, НовыйИндекс);

КонецЦикла;

Возврат СхемаНовогоЗапроса.ПолучитьТекстЗапроса();

КонецФункции

Процедура ОбработатьПакет(СхемаЗапроса, ПакетЗапроса, ТаблицаЗапроса, ИндексПакетаЗапроса = Неопределено)

Если ТипЗнч(ПакетЗапроса) = Тип("ЗапросВыбораСхемыЗапроса") Тогда

ИндексПакетаЗапроса             = ?(ИндексПакетаЗапроса = Неопределено, СхемаЗапроса.ПакетЗапросов.Индекс(ПакетЗапроса), ИндексПакетаЗапроса);
АнализируемыеВременныеТаблицы   = ДоступныеВременныеТаблицыПакета(ПакетЗапроса);
АнализируемыеВыражения          = Новый Массив;

Если НЕ ПустаяСтрока(ПакетЗапроса.ТаблицаДляПомещения) Тогда
ДобавитьСтрокуВТаблицуЗапроса(ТаблицаЗапроса, ПакетЗапроса.ТаблицаДляПомещения, ИндексПакетаЗапроса);
КонецЕсли;

Для Каждого Оператор Из ПакетЗапроса.Операторы Цикл

Для Каждого Источник Из Оператор.Источники Цикл

//Источник

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

//Удаляем найденные ВТ из массива искомых, потому что они уже и так используются

ИспользуемыеВТ    = ТаблицаЗапроса.НайтиСтроки(Новый Структура("ИндексПакетаЗапроса", ИндексПакетаЗапроса));
Для Каждого ИспользуемаяВТ Из ИспользуемыеВТ Цикл
ИндексЭлемента  = АнализируемыеВременныеТаблицы.Найти(ИспользуемаяВТ.ИспользуемаяВременнаяТаблица);
Если ИндексЭлемента <> Неопределено Тогда
АнализируемыеВременныеТаблицы.Удалить(ИндексЭлемента);
КонецЕсли;
КонецЦикла;

//Соединения

Если АнализируемыеВременныеТаблицы.Количество() > 0 Тогда
Для Каждого Соединение Из Источник.Соединения Цикл
ИспользуемыеВТ  = ИспользуемыеВременныеТаблицыВВыражении(АнализируемыеВременныеТаблицы, Соединение.Условие);
Для Каждого ИспользуемаяВТ Из ИспользуемыеВТ Цикл
ДобавитьСтрокуВТаблицуЗапроса(ТаблицаЗапроса, ИспользуемаяВТ, ИндексПакетаЗапроса);
ИндексЭлемента  = АнализируемыеВременныеТаблицы.Найти(ИспользуемаяВТ);
Если ИндексЭлемента <> Неопределено Тогда
АнализируемыеВременныеТаблицы.Удалить(ИндексЭлемента);
КонецЕсли;
КонецЦикла;
Если АнализируемыеВременныеТаблицы.Количество() = 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;

КонецЦикла;

//Отборы

Если АнализируемыеВременныеТаблицы.Количество() > 0 Тогда
Для Каждого Отбор Из Оператор.Отбор Цикл
ИспользуемыеВТ  = ИспользуемыеВременныеТаблицыВВыражении(АнализируемыеВременныеТаблицы, Отбор);
Для Каждого ИспользуемаяВТ Из ИспользуемыеВТ Цикл
ДобавитьСтрокуВТаблицуЗапроса(ТаблицаЗапроса, ИспользуемаяВТ, ИндексПакетаЗапроса);
ИндексЭлемента  = АнализируемыеВременныеТаблицы.Найти(ИспользуемаяВТ);
Если ИндексЭлемента <> Неопределено Тогда
АнализируемыеВременныеТаблицы.Удалить(ИндексЭлемента);
КонецЕсли;
КонецЦикла;
Если АнализируемыеВременныеТаблицы.Количество() = 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;

КонецЦикла;

ИначеЕсли ТипЗнч(ПакетЗапроса) = Тип("ЗапросУничтоженияТаблицыСхемыЗапроса") Тогда

УдаляемыеСтроки = ТаблицаЗапроса.НайтиСтроки(Новый Структура("ИспользуемаяВременнаяТаблица", ПакетЗапроса.ИмяТаблицы));
Для Каждого УдаляемаяСтрока Из УдаляемыеСтроки Цикл
ТаблицаЗапроса.Удалить(УдаляемаяСтрока);
КонецЦикла;

КонецЕсли;

КонецПроцедуры

Функция ИспользуемыеВременныеТаблицыВВыражении(АнализируемыеВременныеТаблицы, Выражение)

ИспользуемыеВременныеТаблицыВВыражении  = Новый Массив;

ТекстВыражения  = ВРег(Строка(Выражение));
Если Найти(ТекстВыражения, "ВЫБРАТЬ") <> 0
И Найти(ТекстВыражения, "ИЗ") <> 0
И Найти(ТекстВыражения, "КАК") <> 0 Тогда


Для Каждого ВременнаяТаблица Из АнализируемыеВременныеТаблицы Цикл
КоординатаИспользованияВТ   = Найти(ТекстВыражения, ВРег(ВременнаяТаблица) + " КАК");
Если КоординатаИспользованияВТ <> 0 Тогда
Если Сред(ТекстВыражения, КоординатаИспользованияВТ-1, 1) <> "." Тогда    //отсекаем выражения "Таблица.ИмяВТ КАК ИмяВТ" И "Документ.ИмяВТ КАК ИмяВТ"
ИспользуемыеВременныеТаблицыВВыражении.Добавить(ВременнаяТаблица);
КонецЕсли;
КонецЕсли;
КонецЦикла;

КонецЕсли;

Возврат ИспользуемыеВременныеТаблицыВВыражении;

КонецФункции

Процедура ДобавитьСтрокуВТаблицуЗапроса(ТаблицаЗапроса, ИспользуемаяВременнаяТаблица, ИндексПакетаЗапроса)

//Добавляем, если такой ещё нет

Если ТаблицаЗапроса.НайтиСтроки(Новый Структура("ИспользуемаяВременнаяТаблица, ИндексПакетаЗапроса", ИспользуемаяВременнаяТаблица, ИндексПакетаЗапроса)).Количество() = 0 Тогда
стрТаблицаЗапроса                               = ТаблицаЗапроса.Добавить();
стрТаблицаЗапроса.ИндексПакетаЗапроса           = ИндексПакетаЗапроса;
стрТаблицаЗапроса.ИспользуемаяВременнаяТаблица  = ИспользуемаяВременнаяТаблица;
КонецЕсли;

КонецПроцедуры

Функция ДоступныеВременныеТаблицыПакета(ПакетЗапроса)

ДоступныеВременныеТаблицыПакета    = Новый Массив;

ДоступныеТаблицы    = ПакетЗапроса.ДоступныеТаблицы.Найти("Временные таблицы");
Если ДоступныеТаблицы <> Неопределено Тогда
Для Каждого ДоступнаяТаблица Из ДоступныеТаблицы.Состав Цикл
ДоступныеВременныеТаблицыПакета.Добавить(ДоступнаяТаблица.Имя);
КонецЦикла;
КонецЕсли;

Возврат ДоступныеВременныеТаблицыПакета;

КонецФункции

 

2 Comments

  1. Mi4man

    А почему именно «в местах последнего использования ВТ», а не в конце текста запроса?

    Можете скинуть ссылку на инфу?

    (из личного интереса)

    Reply
  2. Synoecium

    (1) Mi4man, Предполагаю, чтобы дальше можно было использовать имя данной ВТ еще раз в том же запросе, но ниже по тексту. Возможно это снижает нагрузку на память для хранения временных таблиц в SQL, но точно не знаю так ли это. Пару раз возникала такая необходимость, но такая, легкая необходимость, решаемая вручную.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *