Развитие запросов: от простого к простому

Основным механизмом для выборки данных в 1С является механизм запросов. И конечно с развитием возможностей платформы 1С меняются и дополняются возможности и синтаксис запросов.

Рассмотрим, как изменялся синтаксис текстов запросов на простом примере. Проводится документ «Расходная» содержащая в табличной части «Товары» список продаваемых товаров и количество. При проведении такого документа необходимо обеспечить контроль отрицательных остатков хранящихся в регистре накопления остатков «ОстаткиТоваров».

Сформируем запрос к табличной части документа и виртуальной таблице «Остатки» регистра накопления. Учтем возможные дубли строк в документе, для этого произведем группирование записей.

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

Данный запрос не оптимален, так как из регистра накопления извлекаются все данные и уже потом отбираются нужные записи, что значительно увеличивает время работы запроса. Для его оптимизации можно воспользоваться вложенными запросами: Произведем группирование табличной части документа до соединения с таблицей остатков, в параметры виртуальной таблицы передадим список товаров как значение условия для расчета остатков. В итоге запрос примет следующий вид:

               |ВЫБРАТЬ
               | Док.Номенклатура,
               | Док.Количество КАК Док_Количество,
               | ЕСТЬNULL(Рег.КоличествоОстаток,0) КАК Рег_Количество
               |ИЗ
               | (ВЫБРАТЬ
               | Номенклатура, СУММА(Количество) КАК Количество
               | ИЗ Документ.Расходная.Товары
               | ГДЕ Ссылка = &Ссылка
               | СГРУППИРОВАТЬ ПО Номенклатура) КАК Док
               | ЛЕВОЕ СОЕДИНЕНИЕ
               | РегистрНакопления.ОстаткиТоваров.Остатки(,
               | Номенклатура В(ВЫБРАТЬ РАЗЛИЧНЫЕ
               | Номенклатура
               | ИЗ
               | Документ.Расходная.Товары
               | ГДЕ Ссылка = &Ссылка)) КАК Рег
               | ПО
               | Док.Номенклатура = Рег.Номенклатура";

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

Так было раньше. Но 1С не стоит на месте и постоянно совершенствует и дополняет возможности платформы. В одной из редакций платформы в запросах стало возможно использование временных таблиц. Для этого используется объект «Менеджер временных таблиц». Фактически менеджер временных таблиц описывает пространство имен временных таблиц и отвечает за их создание и уничтожение в базе данных.

Изменим запрос для использования временных таблиц. Во временные таблицы поместим сгруппированную табличную часть документа и список товаров для фильтра виртуальных таблиц:

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

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

И наконец, в системе программ 1С: Предприятие 8, начиная с версии 8.1.11, появилось понятие «Пакетные запросы». По своей сути пакетные запросы предоставляют новый подход к формированию сложных запросов.

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

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

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

Перепишем процедуру для использования пакетных запросов:

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

После выполнения запроса в переменную «МассивРезультатов» у нас попадет 3 элемента. Первые два будут содержать число характеризующее количество записей помещенных во временные таблицы ДокТЧ и СписокТоваров, а третий будет содержать выборку с полями «Номенклатура», «Док_Количество» и «Рег_Количество». В переменную «РезультатЗапроса» попадет только выборка.

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

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