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

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

Для отладки запросов 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 не будет опубликован. Обязательные поля помечены *