Алгоритм по переносу вложенных запросов в пакеты

Запросы неудобно отлаживать, если в них есть вложенные запросы. Данный алгоритм помогает отредактировать текст запроса таким образом, чтобы все вложенные запросы перенеслись в пакеты.

Для отладки запросов 1С вложенные запросы очень неудобны. Разработанный набор функций позволяет перенести вложенные запросы в пакеты. Результаты пакетов можно просматривать отдельно с помощью доработанных консолей запросов, которых множество.

Пример использования:

ТекстЗапросаНовый = ВынестиВложенныеТаблицы(ТекстЗапроса);

Пример того, что было:

ВЫБРАТЬ
ВложенныйЗапрос.Ссылка
ИЗ
(ВЫБРАТЬ
Банки.Ссылка КАК Ссылка
ИЗ
Справочник.Банки КАК Банки) КАК ВложенныйЗапрос

Стало:

ВЫБРАТЬ
Банки.Ссылка КАК Ссылка
ПОМЕСТИТЬ Банки
ИЗ
Справочник.Банки КАК Банки
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВложенныйЗапрос.Ссылка
ИЗ
Банки КАК ВложенныйЗапрос

Для примера использования встроил функции в типовую обработку КонсольОтчетов из 1С УПП 1.3, см. приложенный файл:

Также набор функций приложил в отдельном файле txt.

Или можно скачать здесь:

// --
// Функции редактирования запроса

// Функция выноса вложенных таблиц
Функция ВынестиВложенныеТаблицы(Знач ГлобальныйТекстЗапроса)

МассивПакетовЗапросов = Новый Массив;

// Получаем все подзапросы, разделенные ";"
МассивИсключенийСимоволов = Новый Массив;
МассивИсключенийСимоволов.Добавить("""");
ПакетыЗапросов = ПолучитьМассивСловРазделенныхСимволом(ГлобальныйТекстЗапроса, ";", , , МассивИсключенийСимоволов);

// --
// Из ПакетыЗапросов сформируем МассивПакетовЗапросов

// При формировании пакетов имена должны быть уникальны. Вложенные запросы могут иметь неуникальные наименования.
// Для этого ведём массив созданных пакетов, в случае неуникального наименования добавляем порядковый номер
МассивИменПакетов = Новый Массив;

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

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

ПозицияНачала = ПозицияОкончания + 1;
КонецЦикла;
// Из ПакетыЗапросов сформируем МассивПакетовЗапросов
// --

// Сформируем запрос из МассивПакетовЗапросов
ГлобальныйТекстЗапросаНовый = "";
Для Каждого ПакетЗапроса Из МассивПакетовЗапросов Цикл

МассивВырезок = Новый Массив;
ТекстПодзапросов = ПолучитьТекстПодзапросов(ГлобальныйТекстЗапроса, ПакетЗапроса.Подзапросы, МассивВырезок);
ГлобальныйТекстЗапросаНовый = ГлобальныйТекстЗапросаНовый + ?(ГлобальныйТекстЗапросаНовый = "" Или ТекстПодзапросов = "", "", ";") + ТекстПодзапросов;

ТекстПакета = ПолучитьВырезкуТекста(ГлобальныйТекстЗапроса, ПакетЗапроса.ПозицияНачала, ПакетЗапроса.ПозицияОкончания, МассивВырезок);

ГлобальныйТекстЗапросаНовый = ГлобальныйТекстЗапросаНовый + ?(ГлобальныйТекстЗапросаНовый = "" Или ТекстПакета = "", "", ";") + ТекстПакета;
КонецЦикла;

Возврат ГлобальныйТекстЗапросаНовый;

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

// ПозицияВГлобальномТексте - позиция переданного текста в тексте глобального запроса
Функция ПолучитьСтруктуруПодзапросов(Знач ТекстПакетаЗапроса, Знач ПозицияВГлобальномТексте = 0, Знач ГлобальныйТекстЗапроса, МассивИменПакетов)

ТекстДлина = СтрДлина(ТекстПакетаЗапроса);

Структура = Новый Структура;

МассивСпециальныхСимволов = ПолучитьМассивСпециальныхСимволов();
МассивСпециальныхСимволовКромеСкобок = ПолучитьМассивСпециальныхСимволов("()");

// --
// Перебираем текст по символьно
Для НомерБуквы = 1 По ТекстДлина Цикл

// --
// Определяем, что это начало нового запроса

// В целях оптимизации не используем ПолучитьСлово, но могут быть ошибки, поэтому перепроверим через ПолучитьСлово ещё раз внизу
Если Сред(ТекстПакетаЗапроса, НомерБуквы, 8) = "(ВЫБРАТЬ" Тогда

ПрошлоеСлово = ПолучитьСлово(ТекстПакетаЗапроса, НомерБуквы, Истина, Ложь, МассивСпециальныхСимволов);
Если
ПолучитьСлово(ТекстПакетаЗапроса, НомерБуквы, Ложь, Истина, МассивСпециальныхСимволовКромеСкобок) = "(ВЫБРАТЬ"
И
ПрошлоеСлово <> "ВСЕ" И ПрошлоеСлово <> "ОБЪЕДИНИТЬ" И ПрошлоеСлово <> "В" И ПрошлоеСлово <> "ИЕРАРХИИ" Тогда

ПозицияИз = Неопределено;

Для НомерПодбуквы = НомерБуквы + 1 По ТекстДлина Цикл

ТекстПодзапроса = Сред(ТекстПакетаЗапроса, НомерБуквы, НомерПодбуквы - НомерБуквы);

// Определяем позицию ИЗ в запросе
Если ПозицияИз = Неопределено Тогда
Если Сред(ТекстПакетаЗапроса, НомерПодбуквы, 2) = "ИЗ" И СтрЧислоВхождений(ТекстПодзапроса, "(") - СтрЧислоВхождений(ТекстПодзапроса, ")") = 1 Тогда
ПозицияИз = ПозицияВГлобальномТексте + НомерПодбуквы - 1;
ИначеЕсли Сред(ТекстПакетаЗапроса, НомерПодбуквы + 1, 10) = "ОБЪЕДИНИТЬ" Тогда
ПозицияИз = ПозицияВГлобальномТексте + НомерПодбуквы - 1;
КонецЕсли;
КонецЕсли;

// --
// Проверим, что скобки закрылись, значит подзапрос закрывается
Если СтрЧислоВхождений(ТекстПодзапроса, "(") = СтрЧислоВхождений(ТекстПодзапроса, ")") Тогда

// Если ПозицияИз не определена, значит ставим её в конце
Если ПозицияИз = Неопределено Тогда
ПозицияИз = НомерПодбуквы;
КонецЕсли;

// Урезаем скобки по краяем
ТекстПодзапросаБезСкобок = Сред(ТекстПодзапроса, 2, СтрДлина(ТекстПодзапроса) - 2);

Ключ = "Запрос" + Строка(Структура.Количество());

// Получаем имя пакета как слово после КАК во вложенном запросе
НачальнаяПозиция = ПозицияВГлобальномТексте + НомерПодбуквы;
СловоКАК = ПолучитьСлово(ГлобальныйТекстЗапроса, НачальнаяПозиция, , Истина, МассивСпециальныхСимволов, НачальнаяПозиция);
Если СловоКАК = "КАК" Тогда
ИмяПакета = ПолучитьСлово(ГлобальныйТекстЗапроса, НачальнаяПозиция + 4, , Истина, МассивСпециальныхСимволов);
Если ИмяПакета = "" Тогда
ВызватьИсключение("СП. Не смогли определить имя пакета!");
КонецЕсли;
Иначе
ВызватьИсключение("СП. Не смогли определить имя пакета!");
КонецЕсли;

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

Структура.Вставить(Ключ, Новый Структура("ТекстЗапроса, ИмяПакета, ПозицияНачалаСоСкобкой, ПозицияОкончанияСоСкобкой, ПозицияИз, Подзапросы", ТекстПодзапросаБезСкобок, ИмяПакета, ПозицияВГлобальномТексте - 1 + НомерБуквы, ПозицияВГлобальномТексте - 1 + НомерПодбуквы, ПозицияИз));
Структура[Ключ].Подзапросы = ПолучитьСтруктуруПодзапросов(ТекстПодзапросаБезСкобок, ПозицияВГлобальномТексте + НомерБуквы, ГлобальныйТекстЗапроса, МассивИменПакетов);
НомерБуквы = НомерПодбуквы + 1;
Прервать;
КонецЕсли;
// Проверим, что скобки закрылись, значит подзапрос закрывается
// --

КонецЦикла;
КонецЕсли;
КонецЕсли;
// Определяем, что это начало нового запроса
// --

КонецЦикла;
// Перебираем текст по символьно
// --

Возврат Структура;

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

// Функция получает тексты подзапросов из структуры подзапросов
// МассивВырезок - нужен для накопления информации о подзапросах
Функция ПолучитьТекстПодзапросов(Знач ГлобальныйТекстЗапроса, Подзапросы, МассивВырезок)
ТекстЗапроса = "";

Для Каждого Подзапрос Из Подзапросы Цикл

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

// Получаем подзапросы подзапроса, потом получаем текст подзапроса
МассивПодвырезок = Новый Массив;
ТекстПодзапросов = ПолучитьТекстПодзапросов(ГлобальныйТекстЗапроса, Подзапрос.Значение.Подзапросы, МассивПодвырезок);
ТекстЗапроса = ТекстЗапроса + ?(ТекстЗапроса = "" Или ТекстПодзапросов = "", "", ";") + ТекстПодзапросов;

ТекстПодзапроса =
Сред(ГлобальныйТекстЗапроса, Подзапрос.Значение.ПозицияНачалаСоСкобкой + 1, Подзапрос.Значение.ПозицияИз - Подзапрос.Значение.ПозицияНачалаСоСкобкой - 1)
+
" ПОМЕСТИТЬ " + Подзапрос.Значение.ИмяПакета + " "
+
ПолучитьВырезкуТекста(ГлобальныйТекстЗапроса, Подзапрос.Значение.ПозицияИз, Подзапрос.Значение.ПозицияОкончанияСоСкобкой - 1, МассивПодвырезок);

ТекстЗапроса = ТекстЗапроса + ?(ТекстЗапроса = "" Или ТекстПодзапроса = "", "", ";") + ТекстПодзапроса;

//// Объединим массив подвырезок с массивом вырезок
//Для Каждого Элемент Из МассивПодвырезок Цикл
// МассивВырезок.Добавить(Элемент);
//КонецЦикла;

КонецЦикла;

Возврат ТекстЗапроса;
КонецФункции

// Функция получает вырезку из текста с учётом всех фрагментов, которые должны быть вырезаны внутри неё
// МассивВырезок - сожержит элементы - структуры (ПозицияНачала, ПозицияОкончания, ТекстЗапроса = ""), где ТекстЗапроса - будет вставлено вместо вырезаемого текста
Функция ПолучитьВырезкуТекста(ГлобальныйТекстЗапроса, ПозицияНачала, ПозицияОкончания, Знач МассивВырезок)

// --
// Строим МассивТочекТекста

// Массив, элементами которого будет структура(Позиция, ТекстЗапроса = "")
// Если ТекстЗапроса заполнено, то вместо текста между позициями, берём этот текст
МассивТочекТекста = Новый Массив;

МассивТочекТекста.Добавить(Новый Структура("Позиция, ТекстЗапроса", ПозицияНачала, ""));

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

МассивТочекТекста.Добавить(Новый Структура("Позиция, ТекстЗапроса", ПозицияОкончания, ""));

// Строим МассивТочекТекста
// --

// --
// Формируем текст
ТекстЗапроса = "";

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

ПозицияПрошлая = ТочкаТекста.Позиция;
ТекстПрошлый = ТочкаТекста.ТекстЗапроса;
КонецЦикла;
// Формируем текст
// --

Возврат ТекстЗапроса;

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

// СловоИсключение - содержит символы, которые не являются специальными символами
Функция ПолучитьМассивСпециальныхСимволов(СловоИсключение = "")
МассивДляВозврата = Новый Массив;

Массив = Новый Массив;

Массив.Добавить(Символы.ВК);
Массив.Добавить(Символы.ВТаб);
Массив.Добавить(Символы.НПП);
Массив.Добавить(Символы.ПС);
Массив.Добавить(Символы.ПФ);
Массив.Добавить(Символы.Таб);
Массив.Добавить(")");
Массив.Добавить("(");
Массив.Добавить("{");
Массив.Добавить(".");
Массив.Добавить(",");
Массив.Добавить(";");
Массив.Добавить(" ");

Для Каждого Символ Из Массив Цикл
Если Найти(СловоИсключение, Символ) = 0 Тогда
МассивДляВозврата.Добавить(Символ);
КонецЕсли;
КонецЦикла;

Возврат МассивДляВозврата;
КонецФункции

// Функции редактирования запроса
// --

// --
// Дополнительные строковые функции

// Функция разбивает текст на слова и возвращает массив.
// Если нужно получить конкретное количество слов, то
// надо задать параметр НужноСлов.
// В случае если найденных слов меньше показателя НужноСлов, то
// оставшиеся элементы массива будут заданы значением ТекстЗаменыНенайденных.
//
// Параметры
// - ТекстЗапроса:Строка - текст, в котором ищем слова, разделенные символом
// - Символ:Строка - символ разделения
// - НужноСлов:Число - нужно элементов в возвращаемом массиве
// - ТекстЗаменыНенайденных:Строка - текст, в котором ищем слова, разделенные символом
//  - СимволыКоторыеДолжныЗакрываться:Массив - массив символов, которые в нутри слова должны иметь четное количество или 0. Например в слове не может быть одной открывающей кавычки
//
// Возвращаемое значение:
// МассивСлов:Массив – массив, элементами которого являются части переданного в функцию текст
//
Функция ПолучитьМассивСловРазделенныхСимволом(Знач ТекстЗапроса, Символ, НужноСлов = 0, ТекстЗаменыНенайденных = "", СимволыКоторыеДолжныЗакрываться = Неопределено) Экспорт

// --
// Массив слов для возврата
МассивСлов = Новый Массив();
// --

// --
// Предварительно удалаем повтор заданного символа в слове
ТекстЗапроса = УдалитьДублирующиеСимволы(ТекстЗапроса, Символ);
// --

// --
// Находим позицию первого Символа
ПозицияСимвола = Найти(ТекстЗапроса, Символ);
// --

// --
// Перебираем все позиции Символа
Пока ПозицияСимвола <> 0 Цикл

// Получаем слово и заносим его в массив
Слово = Лев(ТекстЗапроса, ПозицияСимвола - 1);

// --
// Проверка на СимволыДолжныЗакрываться
Если СимволыКоторыеДолжныЗакрываться <> Неопределено Тогда
ВыйтиИзВерхнегоЦикла = Ложь;

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

Если ВыйтиИзВерхнегоЦикла = Истина Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
// Проверка на СимволыДолжныЗакрываться
// --

МассивСлов.Добавить(Слово);

// Уменьшаем количество НужноСлов
НужноСлов = НужноСлов - 1;

// Обрезаем текст
ТекстЗапроса = Прав(ТекстЗапроса, СтрДлина(ТекстЗапроса) - ПозицияСимвола);

// Находим позицию следующего Символа
ПозицияСимвола = Найти(ТекстЗапроса, Символ);

КонецЦикла;
// --

// --
// Добавляем последнее слово
МассивСлов.Добавить(ТекстЗапроса);
// --

// --
// Дополняем при необходимости массив пустыми значениями
Для Нумератор = 1 По НужноСлов Цикл
МассивСлов.Добавить(ТекстЗаменыНенайденных);
КонецЦикла;
// --

// --
// Возвращаем массив
Возврат МассивСлов;
// --

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

// Функция удаления дублирующих символов в строке
//
// Параметры
// - ТекстЗапроса:Строка - строка, из которой удаляем дублирующие символы
// - ТекстЗапроса:Символ - строка, которая является разделительным символом в строку замены
// - НужноСлов:Число - нужно слов, разделенных в данной строке заданным символом
// - ТекстЗаменыНенайденных:Строка - строки, которые добавляются для достижения нужного количества слов
//
// Возвращаемое значение:
// ТекстЗапроса:Строка – отредактированная строка ТекстЗапроса
//
//&НаСервере
Функция УдалитьДублирующиеСимволы(Знач ТекстЗапроса, ТекстУдаления) Экспорт

// --
// Определяем длину текста удаления
ДлинаТекстаУдаленияТекст = СтрДлина(ТекстУдаления);
// --

// --
// Удаляем дублирующие символы
Пока Найти(ТекстЗапроса, ТекстУдаления + ТекстУдаления) <> 0 Цикл
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ТекстУдаления + ТекстУдаления, ТекстУдаления);
КонецЦикла;
// --

// --
// Обрезаем пробелы
ТекстЗапроса = СокрЛП(ТекстЗапроса);
// --

// --
// Удалям заданный символ из начала и конца строки: слева и справа
// Слева
Если Лев(ТекстЗапроса, ДлинаТекстаУдаленияТекст) = ТекстУдаления Тогда
ТекстЗапроса = Прав(ТекстЗапроса, СтрДлина(ТекстЗапроса) - ДлинаТекстаУдаленияТекст);
КонецЕсли;

// Справа
Если Прав(ТекстЗапроса, ДлинаТекстаУдаленияТекст) = ТекстУдаления Тогда
ТекстЗапроса = Лев(ТекстЗапроса, СтрДлина(ТекстЗапроса) - ДлинаТекстаУдаленияТекст);
КонецЕсли;
// --

// --
// Возвращаем отредактированный текст
Возврат ТекстЗапроса;
// --

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

// Разбивает строку вида ключ=значение;ключ2=значение;
// где СимволМеждуКлючомИЗначением - =, СимволМеждуЗначениемИКлючом - ;
Функция ПолучитьСтрктуруЗначенийИзСтроки(Знач ТекстЗапроса, СимволМеждуКлючомИЗначением, СимволМеждуЗначениемИКлючом, СимволыКоторыеДолжныЗакрываться)

Структура = Новый Структура;

МассивКлючЗначение = ПолучитьМассивСловРазделенныхСимволом(ТекстЗапроса, СимволМеждуЗначениемИКлючом, , , СимволыКоторыеДолжныЗакрываться);
Для Каждого КлючЗначение Из МассивКлючЗначение Цикл
Массив = ПолучитьМассивСловРазделенныхСимволом(КлючЗначение, СимволМеждуКлючомИЗначением, , , СимволыКоторыеДолжныЗакрываться);
Если Массив.Количество() = 2 Тогда
Структура.Вставить(Массив[0], Массив[1]);
Иначе
// Что-то распарсили не так, возможно надо вызвать ошибку
КонецЕсли;
КонецЦикла;

Возврат Структура;

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

// Получает следующее слово в тексте
// ОбходВперед - Слово должно быть за начальной позицией или до начальной позиции
// НачальнаяПозицияСкорректированная - служит для возврата скорректированной начальной позиции
Функция ПолучитьСлово(ТекстЗапроса, Знач НачальнаяПозиция, КорриктороватьНачальнуюПозицию = Ложь, ОбходВперед = Истина, МассивСпециальныхСимволов, НачальнаяПозицияСкорректированная = Неопределено)

ДлинаТекста = СтрДлина(ТекстЗапроса);

// --
// Подредактировать начальную позицию.
// Например, если начальная позиия не верна, равна пробелу, то нужно увеличить вправо начальную позицию, чтобы добравться до первой буквы
// и потом получить начиная с неё слово
Если КорриктороватьНачальнуюПозицию = Истина Тогда

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

КонецЕсли;

НачальнаяПозицияСкорректированная = НачальнаяПозиция;
// Подредактировать начальную позицию.
// Например, если начальная позиия не верна, равна пробелу, то нужно увеличить вправо начальную позицию, чтобы добравться до первой буквы
// и потом получить начиная с неё слово
// --

// --
// Ищем конец слова
Если ОбходВперед = Истина Тогда
Для НомерБуквы = НачальнаяПозиция По ДлинаТекста Цикл
Если МассивСпециальныхСимволов.Найти(Сред(ТекстЗапроса, НомерБуквы, 1)) <> Неопределено
Или
НомерБуквы = ДлинаТекста
Тогда

// --
// Возвращаем найденное слово
// Если слово последнее в тексте (НомерБуквы = ДлинаТекста), то возвращаем с последним символом
НайденноеСлово = Сред(
ТекстЗапроса,
НачальнаяПозиция,
НомерБуквы - НачальнаяПозиция + ?(НомерБуквы = ДлинаТекста, 1, 0)
);
Возврат НайденноеСлово;
// --

КонецЕсли;
КонецЦикла;
Иначе
НомерБуквы = НачальнаяПозиция;
Пока НомерБуквы <> 0 Цикл
Если МассивСпециальныхСимволов.Найти(Сред(ТекстЗапроса, НомерБуквы, 1)) <> Неопределено
Или
НомерБуквы = 0
Тогда

// --
// Возвращаем найденное слово
// Если слово последнее в тексте (НомерБуквы = ДлинаТекста), то возвращаем с последним символом
НайденноеСлово = Сред(
ТекстЗапроса,
НомерБуквы + ?(НомерБуквы = 0, 0, 1),
НачальнаяПозиция - НомерБуквы
);
Возврат НайденноеСлово;
// --

КонецЕсли;
НомерБуквы = НомерБуквы - 1;
КонецЦикла;
КонецЕсли;

Возврат Неопределено;

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

// Дополнительные строковые функции
// --

 

20 Comments

  1. Bazin

    Интересно, нашел маленький баг, нужно добавить проверку СтрДлина(ИмяПакета) > 0:

    // Проверка на уникальность будущего пакета
    Если МассивИменПакетов.Найти(ИмяПакета) = Неопределено И СтрДлина(ИмяПакета) > 0 Тогда
    МассивИменПакетов.Добавить(ИмяПакета);
    
    
    Reply ↓
  2. ostapchenko.alexandr

    (1) Bazin, Здравствуйте, а как может ИмяПакета быть пустой строкой? Имя пакета берётся из вложенного запроса после слова КАК. Т.е. в запросе

    ВЫБРАТЬ
    ВложенныйЗапрос.Ссылка
    ИЗ
    (ВЫБРАТЬ
    Банки.Ссылка КАК Ссылка
    ИЗ
    Справочник.Банки КАК Банки) КАК ВложенныйЗапрос
    

    Показать

    ВложенныйЗапрос — это ИмяПакета. Разве есть вложенные запросы без КАК … ?

    Reply ↓
  3. Bazin

    Вот пример:

    ВЫБРАТЬ
    ВложенныйЗапрос.Поле1
    ИЗ
    (ВЫБРАТЬ
    ПОДСТРОКА(Банки.Код, 1, 9) КАК Поле1
    ИЗ
    Справочник.Банки КАК Банки
    
    СГРУППИРОВАТЬ ПО
    ПОДСТРОКА(Банки.Код, 1, 9)) КАК ВложенныйЗапрос
    

    Показать

    Reply ↓
  4. Bazin

    А вот вообще без «КАК»

    ВЫБРАТЬ
    ВложенныйЗапрос.Поле1
    ИЗ
    (ВЫБРАТЬ
    1 КАК Поле1
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
    2) КАК ВложенныйЗапрос
    

    Показать

    Reply ↓
  5. Bazin

    И вот такой, «Временная таблица уже существует»:

    ВЫБРАТЬ
    Банки.Ссылка
    ПОМЕСТИТЬ Банки
    ИЗ
    Справочник.Банки КАК Банки
    ;
    
    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВложенныйЗапрос.Ссылка
    ИЗ
    (ВЫБРАТЬ
    Банки.Ссылка КАК Ссылка
    ИЗ
    Банки КАК Банки) КАК ВложенныйЗапрос
    

    Показать

    Reply ↓
  6. ostapchenko.alexandr

    (5) Bazin, Спасибо! Все замечания исправил.

    Reply ↓
  7. DrAku1a

    У меня в отладчике запросов — это реализовано, года так три назад…

    http://infostart.ru/public/190493/#UnwrapQueryToBatch

    Reply ↓
  8. Bazin

    (7) DrAku1a, тонкий есть?

    Reply ↓
  9. kiruha

    (7)Мощная вещь, но к сожалению там обычные формы, на современных конфигурациях(управляемые) не открываются даже если выбрать толстый клиент управляемое приложение. Так что автору плюс за алгоритм, но просьба для в запросник для управляемых добавить

    Reply ↓
  10. uri1978

    (7) DrAku1a, Но при этом сам обработчик толком не работает. Уж извините за пост в защиту автора.

    А если добавить что есть комментарий к Вашему обработчику:

    13.06.2013 12:37

    Функция «Разложить вложенный запрос в пакетный» (ноухау)

    …

    Это довольно интересная функция — нигде такого не встречал.

    Кто не хочет видеть, тот не видит =) В консоли запросов из подсистемы «Инструменты разработчика» это уже давно есть. В описании есть такая функция «Вынести в новый запрос» и даже на картинке видно http://devtool1c.ucoz.ru/index/konsol_zaprosov/0-18

    Ссылка на сообщение

    То совсем весело.

    Эх-х-х если бы Вы его довели до ума, цены бы не было.

    Reply ↓
  11. ostapchenko.alexandr

    Если данный алгоритм не работает на каких-либо примерах, присылайте, буду править.

    Reply ↓
  12. ostapchenko.alexandr

    (9) kiruha, на управляемых формах должно работать, все функции выполняются на сервере

    Reply ↓
  13. tormozit

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

    Reply ↓
  14. tormozit

    (9)

    на современных конфигурациях(управляемые) не открываются даже если выбрать толстый клиент управляемое приложение

    надо выбирать Толстый клиент обычное приложение, а не управляемое.

    Reply ↓
  15. Ovrfox

    А еще добавлять обработки на УФ в конфигурацию. чтобы они открывались.

    Этот вариант не для того, чтобы к конфе на УФ добавить обычную обработку, а как раз наоборот

    К обычной конфе добавить обработку на УФ.

    Т.е не реально использовать эту обработку на УФ конфе, чтобы при этом конфа еще и работала.

    Reply ↓
  16. МимохожийОднако

    ОФФ наверное. Маленькое замечание. В режиме отладки, если не применять специальные инструменты можно использовать метод Запрос.ВыполнитьПакетСПромежуточнымиРезультатами()

    После отладки можно вернуть метод ВыполнитьПакет() на место

    Reply ↓
  17. Bazin

    (13) tormozit, Публикация начинается со слов «Запросы неудобно отлаживать…», а не использовать

    Reply ↓
  18. tormozit

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

    Reply ↓
  19. DrAku1a

    (10) uri1978, Как обычно, руки не доходят сделать. Да и смысл? Есть же хорошая консоль от ИР (в т.ч. мобильных ИР).

    Сам — пользуюсь отладчиком, мне удобно — то, что разделил всё по вкладкам. В итоге — большое окно для редактирования текста запроса, большое окно для просмотра результата, и ничего лишнего.

    Reply ↓
  20. ostapchenko.alexandr

    Исправил

    ПрошлоеСлово <> «ВСЕ» И ПрошлоеСлово <> «ОБЪЕДИНИТЬ» И ПрошлоеСлово <> «В» Тогда

    на

    ПрошлоеСлово <> «ВСЕ» И ПрошлоеСлово <> «ОБЪЕДИНИТЬ» И ПрошлоеСлово <> «В» И ПрошлоеСлово <> «ИЕРАРХИИ» Тогда

    Reply ↓

Leave a Comment

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