Читайте дальше...
Создание и выполнение простой очереди запросов
// Базовое создание очереди запросов и последовательное выполнение
Процедура ВыполнитьОчередьЗапросов()
// Создаем очередь
Очередь = Новый ОчередьЗапросов;
// Добавляем первый запрос
Запрос1 = Новый Запрос;
Запрос1.Текст = "ВЫБРАТЬ * ИЗ Справочник.Номенклатура ГДЕ Наименование = &Наименование";
Запрос1.УстановитьПараметр("Наименование", "Ноутбук");
Очередь.Добавить(Запрос1);
// Добавляем второй запрос
Запрос2 = Новый Запрос;
Запрос2.Текст = "ВЫБРАТЬ * ИЗ Справочник.Контрагенты";
Очередь.Добавить(Запрос2);
// Выполняем все запросы
Результаты = Очередь.Выполнить();
// Обрабатываем результаты
Для Индекс = 0 По Результаты.Количество() - 1 Цикл
Результат = Результаты[Индекс];
Если Результат.Выполнен() Тогда
Выборка = Результат.Выгрузить();
Сообщить("Результат запроса " + Строка(Индекс) + ": " + Строка(Выборка.Количество()));
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Очередь запросов с зависимостями
// Выполнение запросов, где второй зависит от результата первого
Процедура ВыполнитьЗапросыСЗависимостями()
Очередь = Новый ОчередьЗапросов;
// Первый запрос - получаем контрагентов с долгом
Запрос1 = Новый Запрос;
Запрос1.Текст = "
|ВЫБРАТЬ
| Контрагенты.Ссылка КАК Контрагент
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Долг > 0";
// Второй запрос использует результат первого
Запрос2 = Новый Запрос;
Запрос2.Текст = "
|ВЫБРАТЬ
| Реализация.Контрагент,
| СУММА(Реализация.Сумма) КАК Сумма
|ИЗ
| Документ.РеализацияТоваров.РеализацияТоваров КАК Реализация
|ГДЕ
| Реализация.Контрагент В (&Контрагенты)
|СГРУППИРОВАТЬ ПО
| Реализация.Контрагент";
Очередь.Добавить(Запрос1);
Очередь.Добавить(Запрос2);
// Выполняем
Результаты = Очередь.Выполнить();
Если Результаты.Количество() >= 2 Тогда
ВыборкаКонтрагентов = Результаты[0].Выгрузить();
// Формируем массив ссылок для параметра
МассивКонтрагентов = Новый Массив;
Для Каждого Стр Из ВыборкаКонтрагентов Цикл
МассивКонтрагентов.Добавить(Стр.Контрагент);
КонецЦикла;
// Устанавливаем параметр для второго запроса
Запрос2.УстановитьПараметр("Контрагенты", МассивКонтрагентов);
// Перевыполняем очередь
Результаты = Очередь.Выполнить();
// Обрабатываем результат
ВыборкаСумм = Результаты[1].Выгрузить();
Для Каждого Стр Из ВыборкаСумм Цикл
Сообщить("Контрагент: " + Стр.Контрагент + ", сумма: " + Стр.Сумма);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Пакетное выполнение запросов с обработкой ошибок
// Выполнение очереди запросов с обработкой ошибок каждого запроса
Функция ВыполнитьОчередьСОбработкойОшибок(Очередь)
РезультатыВыполнения = Новый Соответствие;
Для НомерЗапроса = 0 По Очередь.Количество() - 1 Цикл
Запрос = Очередь.Получить(НомерЗапроса);
Попытка
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
РезультатыВыполнения.Вставить(НомерЗапроса, "Пустой результат");
Продолжить;
КонецЕсли;
Выборка = РезультатЗапроса.Выгрузить();
РезультатыВыполнения.Вставить(НомерЗапроса, Выборка);
Исключение
// Логируем ошибку
Сообщить("Ошибка в запросе " + Строка(НомерЗапроса) + ": " + ОписаниеОшибки());
РезультатыВыполнения.Вставить(НомерЗапроса, Неопределено);
КонецПопытки;
КонецЦикла;
Возврат РезультатыВыполнения;
КонецФункции
// Пример использования
Процедура ТестВыполненияОчереди()
Очередь = Новый ОчередьЗапросов;
// Корректный запрос
Запрос1 = Новый Запрос;
Запрос1.Текст = "ВЫБРАТЬ ПЕРВЫЕ 5 * ИЗ Справочник.Номенклатура";
Очередь.Добавить(Запрос1);
// Ошибочный запрос (несуществующая таблица)
Запрос2 = Новый Запрос;
Запрос2.Текст = "ВЫБРАТЬ * ИЗ НесуществующийСправочник";
Очередь.Добавить(Запрос2);
// Еще один корректный запрос
Запрос3 = Новый Запрос;
Запрос3.Текст = "ВЫБРАТЬ КОЛИЧЕСТВО(*) КАК КолИЗ Справочник.Склады";
Очередь.Добавить(Запрос3);
Результаты = ВыполнитьОчередьСОбработкойОшибок(Очередь);
// Анализируем результаты
Для Каждого КлючЗначение Из Результаты Цикл
Если КлючЗначение.Значение = Неопределено Тогда
Сообщить("Запрос " + КлючЗначение.Ключ + " завершился с ошибкой");
ИначеЕсли ТипЗнч(КлючЗначение.Значение) = Тип("Строка") Тогда
Сообщить("Запрос " + КлючЗначение.Ключ + ": " + КлючЗначение.Значение);
Иначе
Сообщить("Запрос " + КлючЗначение.Ключ + " вернул " +
Строка(КлючЗначение.Значение.Количество()) + " записей");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Использование очереди запросов с временными таблицами
// Создание и выполнение очереди запросов, обменивающихся временными таблицами
Процедура ОчередьСВременнымиТаблицами()
МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
// Первый запрос - создает временную таблицу
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
Запрос1.Текст = "
|ВЫБРАТЬ
| Номенклатура.Ссылка КАК Товар,
| Номенклатура.Наименование КАК Наименование
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ЛОЖЬ";
Запрос1.Выполнить();
// Второй запрос использует временную таблицу
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
Запрос2.Текст = "
|ВЫБРАТЬ
| ВТ_Товары.Товар,
| ВТ_Товары.Наименование,
| Остатки.Количество Остаток
|ИЗ
| ВТ_Товары КАК ВТ_Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, Товар = ВТ_Товары.Товар) КАК Остатки
| ПО ВТ_Товары.Товар = Остатки.Товар";
// Третий запрос - агрегация по временной таблице
Запрос3 = Новый Запрос;
Запрос3.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
Запрос3.Текст = "
|ВЫБРАТЬ
| СУММА(Остатки.Остаток) КАК ОбщийОстаток
|ИЗ
| ВТ_Остатки КАК Остатки";
// Добавляем в очередь
Очередь = Новый ОчередьЗапросов;
Очередь.Добавить(Запрос2);
Очередь.Добавить(Запрос3);
// Выполняем
Результаты = Очередь.Выполнить();
Если Результаты.Количество() > 0 Тогда
ВыборкаОстатков = Результаты[0].Выгрузить();
Для Каждого Стр Из ВыборкаОстатков Цикл
Сообщить(Стр.Наименование + ": остаток = " + Стр.Остаток);
КонецЦикла;
Если Результаты.Количество() > 1 Тогда
ВыборкаИтога = Результаты[1].Выгрузить();
Сообщить("Общий остаток: " + ВыборкаИтога[0].ОбщийОстаток);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Асинхронное выполнение очереди запросов
// Асинхронное выполнение запросов с использованием фоновых заданий
Процедура ВыполнитьОчередьАсинхронно()
// Создаем структуру с параметрами фонового задания
ПараметрыЗадания = Новый Структура;
// Формируем запросы для фонового выполнения
Запрос1 = Новый Запрос;
Запрос1.Текст = "ВЫБРАТЬ * ИЗ РегистрБухгалтерии.Хозрасчет.Остатки()";
Запрос2 = Новый Запрос;
Запрос2.Текст = "ВЫБРАТЬ * ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки()";
Очередь = Новый ОчередьЗапросов;
Очередь.Добавить(Запрос1);
Очередь.Добавить(Запрос2);
// Сериализуем очередь для передачи в фоновое задание
АдресХранилища = ПоместитьВХранилище(Очередь);
ПараметрыЗадания.Вставить("АдресОчереди", АдресХранилища);
// Создаем фоновое задание
ФоновоеЗадание = РасширенияРаботыСФоновымиЗаданиями.ВыполнитьФоновоеЗадание(
"ВыполнитьОчередьВФоне",
ПараметрыЗадания,
"Выполнение очереди запросов",
УникальныйИдентификатор);
Если ФоновоеЗадание = Неопределено Тогда
Сообщить("Не удалось создать фоновое задание");
Иначе
Сообщить("Фоновое задание запущено, идентификатор: " + ФоновоеЗадание);
КонецЕсли;
КонецПроцедуры
// Эта процедура выполняется в фоновом задании
Процедура ВыполнитьОчередьВФоне(ПараметрыЗадания)
АдресХранилища = ПараметрыЗадания.АдресОчереди;
Очередь = ПолучитьИзХранилища(АдресХранилища);
Если Очередь = Неопределено Тогда
ЗаписьЖурналаРегистрации("Ошибка очереди", "Не удалось восстановить очередь");
Возврат;
КонецЕсли;
Попытка
Результаты = Очередь.Выполнить();
// Сохраняем результаты
ИмяФайла = "РезультатыЗапросов_" + Формат(ТекущаяДата(), "ДФ=yyyyMMddHHmmss") + ".xml";
ЗаписатьXML(ИмяФайла, Результаты);
ЗаписьЖурналаРегистрации("Очередь запросов", "Успешно выполнено " +
Строка(Очередь.Количество()) + " запросов");
Исключение
ЗаписьЖурналаРегистрации("Ошибка очереди", ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Функция ЗаписатьXML(ИмяФайла, Данные)
// Сохранение результатов в XML-файл
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайла);
ЗаписьXML.ЗаписатьБезОбработки(Данные);
ЗаписьXML.Закрыть();
Возврат Истина;
КонецФункции
Процедура ЗаписьЖурналаРегистрации(Событие, Комментарий)
// Запись в журнал регистрации
Запись = Новый ЗаписьЖурналаРегистрации;
Запись.Событие = Событие;
Запись.Комментарий = Комментарий;
Запись.Записать();
КонецПроцедуры
Динамическое построение очереди запросов
// Построение и выполнение очереди на основе массива условий
Функция ПостроитьОчередьПоУсловиям(МассивУсловий)
Очередь = Новый ОчередьЗапросов;
БазовыйТекст = "
|ВЫБРАТЬ
| Счет.Ссылка КАК Счет,
| Счет.Наименование КАК Наименование,
| Сальдо.Сумма КАК Сальдо
|ИЗ
| ПланСчетов.Хозрасчет КАК Счет
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.Хозрасчет.Остатки(,&ДатаОтчета) КАК Сальдо
| ПО Счет.Ссылка = Сальдо.Счет
|ГДЕ
| &Условия";
Для Каждого Условие Из МассивУсловий Цикл
КопияЗапроса = Новый Запрос;
КопияЗапроса.Текст = БазовыйТекст;
КопияЗапроса.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
КопияЗапроса.УстановитьПараметр("Условия", Условие);
Очередь.Добавить(КопияЗапроса);
КонецЦикла;
Возврат Очередь;
КонецФункции
// Пример использования
Процедура АнализСчетовПоУсловиям()
// Массив различных условий фильтрации
Условия = Новый Массив;
Условия.Добавить("Счет.Вид = ЗНАЧЕНИЕ(ВидСчета.Активный)");
Условия.Добавить("Счет.Вид = ЗНАЧЕНИЕ(ВидСчета.Пассивный)");
Условия.Добавить("Счет.ПризнакЗабалансового = ИСТИНА");
Условия.Добавить("Сальдо.Сумма > 0");
Условия.Добавить("Сальдо.Сумма < 0");
// Создаем очередь из 5 запросов
Очередь = ПостроитьОчередьПоУсловиям(Условия);
// Выполняем
Результаты = Очередь.Выполнить();
// Анализируем
Для Инд = 0 По Результаты.Количество() - 1 Цикл
Выборка = Результаты[Инд].Выгрузить();
Сообщить("Условие " + Условия[Инд] + " дало " + Строка(Выборка.Количество()) + " счетов");
// Дополнительная обработка
Если Выборка.Количество() > 0 Тогда
Для Каждого Стр Из Выборка Цикл
// Обработка каждого счета
Продолжить;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Очередь запросов с контролем timeout и приоритетов
// Выполнение очереди с ограничением времени выполнения
Функция ВыполнитьОчередьСТаймаутом(Очередь, МаксВремяМс = 30000) Экспорт
Результаты = Новый Массив;
ВремяНачала = ТекущаяУниверсальнаяДатаВМиллисекундах();
Для Ном = 0 По Очередь.Количество() - 1 Цикл
// Проверяем таймаут
ТекущееВремя = ТекущаяУниверсальнаяДатаВМиллисекундах();
Если (ТекущееВремя - ВремяНачала) > МаксВремяМс Тогда
Сообщить("Превышен таймаут выполнения очереди. Выполнено " +
Строка(Ном) + " из " + Строка(Очередь.Количество()));
Прервать;
КонецЕсли;
Запрос = Очередь.Получить(Ном);
// Устанавливаем таймаут запроса
Попытка
Запрос.УстановитьТаймаут(МаксВремяМс / Очередь.Количество());
Результат = Запрос.Выполнить();
Результаты.Добавить(Результат);
Исключение
Сообщить("Ошибка выполнения запроса " + Строка(Ном) + ": " + ОписаниеОшибки());
Результаты.Добавить(Неопределено);
КонецПопытки;
КонецЦикла;
Возврат Результаты;
КонецФункции
// Очередь с приоритетами
Процедура ОчередьСПриоритетами()
// Создаем структуру запросов с приоритетами
Запросы = Новый СписокЗначений;
// Высокоприоритетный запрос (приоритет 1)
З1 = Новый Запрос;
З1.Текст = "ВЫБРАТЬ * ИЗ Справочник.Пользователи";
Запросы.Добавить(З1, "Пользователи", 1);
// Средний приоритет (приоритет 2)
З2 = Новый Запрос;
З2.Текст = "ВЫБРАТЬ * ИЗ Справочник.Номенклатура";
Запросы.Добавить(З2, "Номенклатура", 2);
// Низкий приоритет (приоритет 3)
З3 = Новый Запрос;
З3.Текст = "ВЫБРАТЬ * ИЗ Справочник.Контрагенты";
Запросы.Добавить(З3, "Контрагенты", 3);
// Сортируем по приоритету
Запросы.СортироватьПоЗначению(СортировкаПоЗначению.Возр);
// Создаем очередь в порядке приоритета
Очередь = Новый ОчередьЗапросов;
Для Инд = 0 По Запросы.Количество() - 1 Цикл
Очередь.Добавить(Запросы[Инд].Значение);
КонецЦикла;
// Выполняем с таймаутом 10 секунд
Результаты = ВыполнитьОчередьСТаймаутом(Очередь, 10000);
// Обработка результатов
Для Инд = 0 По Результаты.Количество() - 1 Цикл
Если Результаты[Инд] <> Неопределено И НЕ Результаты[Инд].Пустой() Тогда
Выборка = Результаты[Инд].Выгрузить();
Сообщико("Запрос " + Запросы[Инд].Представление + ": " +
Строка(Выборка.Количество()) + " записей");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Примечания
// Важные особенности работы с очередью запросов:
// 1. Очередь запросов позволяет последовательно выполнять несколько запросов
// 2. При использовании временных таблиц необходимо установить общего МенеджераВременныхТаблиц
// 3. Метод Выполнить() возвращает массив результатов в порядке добавления запросов
// 4. При ошибке в одном запросе, выполнение очереди продолжается
// 5. Для больших объемов данных рекомендуется использовать асинхронное выполнение
// 6. Для каждого запроса можно установить свой таймаут выполнения
// 7. Очередь не поддерживает параллельное выполнение - запросы выполняются строго последовательно
// 8. При работе с транзакциями учитывайте, что очередь по умолчанию не создает транзакцию
// 9. Для отладки очереди сохраняйте тексты запросов перед выполнением
// 10. В фоновых заданиях очередь запросов может работать с ограничениями по времени СУБД








