Примеры работы с очередью запросов

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

Создание и выполнение простой очереди запросов

// Базовое создание очереди запросов и последовательное выполнение
Процедура ВыполнитьОчередьЗапросов()

    // Создаем очередь
    Очередь = Новый ОчередьЗапросов;
    
    // Добавляем первый запрос
    Запрос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. В фоновых заданиях очередь запросов может работать с ограничениями по времени СУБД

Поделиться с друзьями
Smirnov code
Добавить комментарий