Основная страница

 

Глава 34-3
Язык Запросов


 

 

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

Печать каталога товаров

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

//--------------------------------

Процедура ПечатьСправочника()

   // Процедура печати полного справочника товаров

   Перем Запрос, ТекстЗапроса, Таб;

   //Создание объекта типа Запрос

   Запрос = СоздатьОбъект("Запрос");

   ТекстЗапроса = "//{{ЗАПРОС(Печать)

   |Товар = Справочник.Товары.ТекущийЭлемент;

   |Группировка Товар Упорядочить по Товар.МинЗапас;

   |"//}}ЗАПРОС

   // Если ошибка в запросе, то выход из процедуры

   Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда

      Возврат;

   КонецЕсли;

   // Заполнение выходных форм данными запроса

   // Создание Таблицы для выходного отчета

   Таб = СоздатьОбъект("Таблица");

   Таб.ВывестиСекцию("Отчет");

   Пока Запрос.Группировка("Товар") = 1 Цикл

      // Заполнение полей Товар

      Если Запрос.Товар.ЭтоГруппа() = 1 Тогда

         Таб.ВывестиСекцию("Группа");

      Иначе

         Таб.ВывестиСекцию("Товар");

      КонецЕсли;

   КонецЦикла;

   //Отображение выходного отчета

   Таб.ТолькоПросмотр(1);

   Таб.Опции(0, 0, 4, 0);

   Таб.Показать("Список товаров по каталогу", "");

КонецПроцедуры

Отчет по неходовым товарам

Далее приведен пример нетривиального использования запроса для просмотра одновременно многих видов документов. Цель данной процедуры — вывести в отчет перечень неходовых товаров, которые совсем не продавались за заданный период и показать в каждой строке текущий остаток и стоимость этих товаров. В данном примере запрос формируется с целью определить, что в него не вошло.

Процедура Сформировать()

   Перем Запрос, ТекстЗапроса, Таб;

   Рег = СоздатьОбъект("Регистр.ОстаткиТовара");

   Запрос = СоздатьОбъект("Запрос");

   ТекстЗапроса = "//{{ЗАПРОС(Сформировать)

   |Период С ДатаНачала По ДатаКонца;

   |ТОВАР = Документ.РасходнаяБН.Товар,

   |   Документ.РасходнаяКредит.Товар, Документ.РасходнаяНал.Товар,

   |   Документ.РасходнаяРеализ.Товар, Документ.Счет.Товар;

   |Группировка ТОВАР упорядочить по ТОВАР.Наименование без групп;

   |"//}}ЗАПРОС

   ;

   Если ДатаКонца >= ПолучитьДатуТА() Тогда

      ТекстЗапроса = ТекстЗапроса + "Период С ДатаНачала;";

   Иначе

      ТекстЗапроса = ТекстЗапроса + "Период С ДатаНачала По ДатаКонца;";

      Per.ВременныйРасчет();

      РассчитатьРегистрыНа(ДатаКонца);

   КонецЕсли;

   // Выполнение Запроса

   Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда

      Возврат;

   КонецЕсли;

   Тов = СоздатьОбъект("Справочник.Товары");

   // обход включая группировки

   Тов.ВключатьПодчиненные(1);

   // упорядочить по наименованиям

   Тов.ПорядокНаименозаний();

   ИтогоОстаток = 0;

   ИтогоСумма = 0;

   Таб = СоздатьОбъект("Таблица");

   Таб.ВывестиСекцию("Отчет");

   Состояние("В отчет выведено " + ЧислоСтрок + " строк.");

   // Запускаем полный цикл по товарам Справочника

   Тов.ВыбратьЭлементы();

   Пока Тов.ПолучитьЭлемент() > 0 Цикл

      Флаг = 0;

      Товар = Тов.ТекущийЭлемент();

      Если Товар.ЭтоГруппа() = 1 Тогда

         Продолжить;

      КонецЕсли;

      // Здесь пытаемся получить из Запроса информацию о товаре,

      // но используем просто сам факт того, что товар попал во

      // временный набор данных Запроса.

      // Если товар есть в Запросе, то значит он упоминался в

      // каких то документах,

      // иначе — товар не пользуется спросом — неходовой.

      Если Запрос.Получить(Товар) = 1 Тогда

         Продолжить;

      КонецЕсли;

      // находим остатки неходового товара на складе

      Рег.СводныеОстатки(Товар, );

      ТекОстаток = Рег.ОстатокТовара;

      ТекСумма = Рег.БазоваяСтоимость;

      Если ТекОстаток = 0 Тогда

         Продолжить;

      КонецЕсли;

      Таб.ВывестиСекцию("Товар");

      ИтогоОстаток = ИтогоОстаток + ТекОстаток;

      ИтогоСумма = ИтогоСумма + ТекСумма;

   КонецЦикла;

   Таб.ВывестиСекцию("Итоги");

   Таб.ТолькоПросмотр(1);

   Таб.Опции(0, 0, 3, 0);

   Таб.Показать("Отчет о неходовых товарах", "");

КонецПроцедуры

ДатаКонца = РабочаяДата();

ДатаНачала = ДатаКонца — Константа.ПериодАнализа;

Отчет по регистру с точностью до строки документа

Далее приведен пример использования запроса для просмотра регистра с выборкой документов, производивших движение данного регистра, с доступом к каждой строке этих документов. Цель данной процедуры — сформировать отчет, чтобы по каждому из видов топлива, отпущенных за заданный период покупателям, показать перечень номеров автомашин, которыми производилась отгрузка. Особенностью данного отчета является то, что номера автомашин не являются измерениями регистра, а зафиксированы в каждой строке отпускного документа.

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

Процедура Сформировать()

   Перем Запрос, ТекстЗапроса, Таб;

   //Создание объекта типа Запрос

   Запрос = СоздатьОбъект("Запрос");

   ТекстЗапроса = "//{{ЗАПРОС(Сформировать)

   |Период с НачДата по КонДата;

   |ВидТоплива = Регистр.ПокупателиКолво.ВидыТоплива;

   |Вес = Регистр.ПокупателиКолво.Кг;

   |Покуп = Регистр.ПокупателиКолво.Покупатели;

   |Док = Регистр.ПокупателиКолво.ТекущийДокумент;

   |Ном = Регистр.ПокупателиКолво.НомерСтроки;

   |Группировка ВидТоплива;         //по измерению Регистра

   |Группировка Док;                // по документам, двигавшим Регистр

   |Группировка Ном;                //по номерам строк документов

   |Функция ВсегоКолво = КонОст(Вес);

   |Функция ПриходКолво = Приход(Вес);

   |Условие(Покуп = ВыбПокупатель);

   |"//}}ЗАПРОС

   ;

   // Если ошибка в запросе, то выход из процедуры

   Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда

      Возврат;

   КонецЕсли;

   // Заполнение выходных форм данными Запроса

   Таб = СоздатьОбъект("Таблица");

   Таб.ИсходнаяТаблица("Таблица1");

   Таб.ВывестиСекцию("Шапка");

   Пока Запрос.Группировка(1) = 1 Цикл

      // Отображаем значение измерения — ВидТоплива

      Таб.ВывестиСекцию("ВидТоплива");

      // запускаем вложенную группировку по документам,

      // которые производили движения Регистра

      Пока Запрос.Группировка(2) = 1 Цикл

         Док1 = Запрос.Док;

         // отфильтруем нужные нам документы

         Если НЕ(Док1.Вид() = "Продажа") Тогда

            Продолжить;

         КонецЕсли;

         // запускаем вложенную группировку по строкам

         // документа, связанным с движениями регистра

         Пока Запрос.Группировка(3) = 1 Цикл

            // Получаем в документе строку по найденному номеру

            Док1.ПолучитьСтрокуПоНомеру(Запрос.Ном);

            Таб.ВывестиСекцию("Строка");

         КонецЦикла;

      КонецЦикла;

   КонецЦикла;

   // Вывод заполненной формы

   Таб.Опции(О, О, 0, 0);

   Таб.ТолькоПросмотр(1);

   Таб.Показать("", "");

КонецПроцедуры

Анализ счета

Приводим пример использования запроса для работы с бухгалтерскими операциями и проводками. Запрос обрабатывает корреспонденции счета Сч по счетам КорСч за расчетный период. Значение счета для анализа задается в диалоге установкой ВыбСч.

Процедура АнализСчета()

   Перем Запрос, ТекстЗапроса, Таб;

   //Создание объекта типа Запрос

   Запрос = СоздатьОбъект("Запрос");

   ТекстЗапроса = "//{{ЗАПРОС(Сформировать)

   |Период с ДатаС по ДатаПо;

   |Сч = Операция.Счет;

   |КорСч = Операция.КорСчет;

   |Сумма = Операция.Сумма;

   |Группировка Сч упорядочить по Сч.Код;

   |Группировка КорСч упорядочить по КорСч.Код;

   |Функция КорДо = КорДО(Сумма);

   |Функция КорКо = КорКО(Сумма);

   |Условие(Сч = ВыбСч);

   |"//}}ЗАПРОС

   ;

   // Если ошибка в запросе, то выход из процедуры

   Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда

      Возврат;

   КонецЕсли;

   // Подготовка к заполнению выходных форм данными запроса

   Таб = СоздатьОбъект("Таблица");

   Таб.ИсходнаяТаблица("Сформировать");

   // Заполнение полей "Заголовок"

   Таб.ВывестиСекцию("Заголовок");

   Состояние("Заполнение выходной таблицы...");

   Пока Запрос.Группировка("Сч") = 1 Цикл

      // Заполнение полей Сч

      Таб.ВывестиСекцию("Сч");

      Пока Запрос.Группировка("КорСч") = 1 Цикл

         // Заполнение полей КорСч

         Таб.ВывестиСекцию("КорСч");

      КонецЦикла;

   КонецЦикла;

   // Заполнение полей "Итого"

   Таб.ВывестиСекцию("Итого");

   // Вывод заполненной формы

   Таб.Опции(1, 0. 1, 0);

   Таб.Показать("Сформировать", "");

КонецПроцедуры

Разработка вложенных отчетов

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

Поскольку каждая ячейка Таблицы может содержать значение, записанное в нее (см. Конфигуратор, редактор таблиц — Свойства ячейки — Текст — поле: Значение), то в программном модуле формы отчета возможно обрабатывать это значение. Обработка значения ячейки Таблицы вызывается системой по клавише <Enter> или по двойному щелчку мышью на какой-либо ячейке (если режим «только просмотр»). Стандартными действиями системы на обработку такого события являются: для документа — открытие документа, для элемента справочника — открытие формы редактирования элемента справочника. Другими словами, стандартные действия системы зависят от типа данных содержащегося в ячейке значения. Однако, это событие возможно перехватить и обработать нестандарным способом. Для этого предназначена предопределенная процедура встроенного языка ОбработкаЯчейкиТаблицы.

Примером нестандартной обработки значения ячейки таблицы может быть, например, вызов на формирование другого отчета. Таким образом, мы можем создавать как бы вложенные отчеты, которые вызываются один из другого, выдавая с каждым разом более детальную информацию. Допустим отчет «Взаиморасчеты» при формировании всегда выводится в кратком виде, когда виден только сводный баланс по контрагенту. Для того, чтобы получить детальный отчет по данному контрагенту, достаточно встать курсором в готовой форме отчета на этого контрагента и нажать клавишу <Enter>. Тогда сработает процедура ОбработкаЯчейкиТаблицы, в которой можно записать вызов формирования детального отчета. А если, кроме того, завести флаг режима отображения, то можно вместо этого показывать карточку этого контрагента из справочника.

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

Пример:

Процедура ПродВсего()

   Перем Запрос, ТекстЗапроса, Таб;

   ДатаКон = ДатаКонца;

   Если ДатаКон >= ПолучитьДатуТА() Тогда

      ДатаКон = Дата(0);

   КонецЕсли;

   //Создание объекта типа Запрос

   Запрос = СоздатьОбъект("Запрос");

   ТекстЗапроса = "//{{ЗАПРОС(ПродВсего)

   |Период с ДатаНачала по ДатаКон;

   |ТОВАР = Документ.РасхНакл.Товар;

   |Сумма_Прод = Документ.РасхНакл.СуммаРуб;

   |КОЛВО_Прод = Документ.РасхНакл.Количество;

   |Группировка ТОВАР;

   |Функция Продано = Сумма(КОЛВО_Прод);

   |Функция СуммаПродано = Сумма(Сумма_Прод);

   |"//}}ЗАПРОС

   ;

   // Если ошибка в запросе, то выход из процедуры

   Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда

      Возврат;

   КонецЕсли;

   // Подготовка к заполнению

   Таб = СоздатьОбъект("Таблица");

   Таб.ИсходнаяТаблица("ТабВсего");

   Таб.ВывестиСекцию("Отчет");

   Пока Запрос.Группировка("Товар") = 1 Цикл

      ПродСумма = Запрос.СуммаПродано;

      Если Запрос.Товар.ЭтоГруппа() = 1 Тогда

         Таб.ВывестиСекцию("Группа");

      Иначе

         Таб.ВывестиСекцию("Товар");

      КонецЕсли;

   КонецЦикла;

   // Вывод заполненной формы

   Таб.ТолькоПросмотр(1);

   Таб.Опции(0, 0, 4, 0);

   Таб.Показать("Продажа товаров ", "");

КонецПроцедуры

В режие исполнения у нас получится такой отчет:

Далее, допустим, мы хотим получить более подробный отчет по конкретному товару, так, чтобы указав на товар в отчете, и дважды нажав кнопку мыши, мы получали отчет по продажам именно этого товара с точностью до каждого клиента. Для этого откроем в Конфигураторе редактор таблицы нашего отчета и в свойствах ячейки (Свойства ячейки — Текст — поле: Значение), отображающей наименование товара, проставим значение ячейки. («Запрос.Товар»)

Далее, в программном модуле напишем процедуру формирования дополнительного отчета.

Продолжение примера:

//*********************************

// Процедура дополнительного отчета

Процедура ПродТовар(ВТовар)

   Перем Запрос, ТекстЗапроса, Таб;

   ДатаКон = ДатаКонца;

   Если ДатаКон >= ПолучитьДатуТА() Тогда

      ДатаКон = Дата(0);

   КонецЕсли;

   //Создание объекта типа Запрос

   Запрос = СоздатьОбъект("Запрос");

   ТекстЗапроса = "//{{ЗАПРОС(ПродТовар)

   |Период с ДатаНачала по ДатаКон;

   |КЛИЕНТ = Документ.РасхНакл.Клиент;

   |ТОВАР = Документ.РасхНакл.Товар;

   |СУММ = Документ.РасхНакл.СуммаВал;

   |КОЛВО = Документ.РасхНакл.Количество;

   |Группировка КЛИЕНТ Упорядочить По КЛИЕНТ.Наименование;

   |Группировка ТОВАР Упорядочить По ТОВАР.Наименование;

   |Функция Продано = Сумма(КОЛВО);

   |Функция ПродСум = Сумма(СУММ);

   |"//}}ЗАПРОС

   ;

   Если ВТовар.Выбран() = 1 Тогда

      Если ВТовар.ЭтоГруппа() = 1 Тогда

         ТекстЗапроса = ТекстЗапроса +

             "Условие(Товар.ПринадлежитГруппе(ВТовар) = 1);";

      Иначе

         ТекстЗапроса = ТекстЗапроса + "Условие (Товар = ВТовар);";

         ФОдинТовар = 1;

      КонецЕсли;

   КонецЕсли;

   // Если ошибка в запросе, то выход из процедуры

   Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда

      Возврат;

   КонецЕсли;

   // Подготовка к заполнению выходных форм

   Таб = СоздатьОбъект("Таблица");

   Таб.ВывестиСекцию("Отчет");

   Пока Запрос.Группировка("Клиент") = 1 Цикл

      Таб.ВывестиСекцию("Клиент");

      Пока Запрос.Группировка("Товар") = 1 Цикл

         Если ФОдинТовар = 0 Тогда

            Таб.ВывестиСекцию("Товар");

         КонецЕсли;

      КонецЦикла;

   КонецЦикла;

   // Вывод заполненной формы

   Таб.ТолькоПросмотр(1);

   Таб.Опции(0, 4, 0, 0);

   Таб.Показать("Продажа товара", "");

КонецПроцедуры

Теперь нам осталось написать предопределенную процедуру, которая возьмет на себя обработку события «двойной щелчок мышью на выбранной ячейке таблицы». Главная задача этой процедуры — определить, что выбрана ячейка, где указано значение товара и вызвать на исполнение процедуру формирования дополнительного отчета, написанную ранее.

Продолжение примера:

//-------------------------

Процедура ОбработкаЯчейкиТаблицы(ЗначЯч, ФлагСтандартнойОбработки)

   Если ТипЗначения(ЗначЯч) = 2 Тогда

      ФлагСтандартнойОбработки = 1;

      Возврат;

   КонецЕсли;

   ПродТовар(ЗначЯч);

КонецПроцедуры

Теперь, в режиме исполнения, если в отчете «Продажи товаров» мы укажем курсором на товар, например, «Сапоги женские» и дважды щелкнем на нем мышью, то сформируется дополнительный подробный отчет «Продажа товара».

Способы оптимизации формирования отчетов

Процесс получения отчетов с использованием запросов можно условно разделить на две фазы: сначала формируется запрос, затем полученные данные выводятся в Таблицу. В данном разделе речь пойдет о второй фазе получения отчета — выводе данных в Таблицу.

Рассмотрим следующий пример. Допустим, требуется вывести в отчет полный перечень товаров со всеми их реквизитами. Для этого сначала формируется запрос с одной Группировкой — "Товар", затем полученные данные выводятся в Таблицу. Ниже приведены три варианта вывода данных в Таблицу.

Вариант 1

В программном модуле текст вывода данных в Таблицу следующий

Пока Запрос.Группировка("Товар") = 1 Цикл

   Таб.ВывестиСекцию("Товар");

КонецЦикла;

Секция "Товар" в Таблице имеет следующий формат:

<Запрос.Товар.Наименование>

<Запрос.Товар.Артикул>

<Запрос.Товар.Цена>

<Запрос.Товар.Валюта>

В первом варианте отображаемые реквизиты товара полностью вычисляются в ячейках Таблицы, причем доступ к каждому реквизиту товара происходит по полному пути: Запрос-Товар-Реквизит. Данный вариант вывода данных в Таблицу самый медленный.

Вариант 2

В программном модуле текст вывода данных в Таблицу следующий

Пока Запрос.Группировка("Товар") = 1 Цикл

   ПечНаим = Запрос.Товар.Наименование;

   ПечАртикул = Запрос.Товар.Артикул;

   ПечЦена = Запрос.Товар.Цена;

   ПечВалюта = Запрос.Товар.Валюта;

   Таб.ВывестиСекцию("Товар1");

КонецЦикла;

Секция "Товар1" в Таблице имеет следующий формат:

<ПечНаим>

<ПечАртикул>

<ПсчЦена>

<ПечВалюта>

Во втором варианте отображаемые реквизиты товара вычисляются в программном модуле, а в ячейках Таблицы размещены простые выражения — ссылки на идентификаторы программного модуля. Доступ к каждому реквизиту товара происходит по полному пути: Запрос-Товар-Реквизит. В данном варианте фаза вывода данных в Таблицу работает быстрее первого варианта (выигрыш ~20%), т. к. в программном модуле выражения вычисляются существенно быстрее, чем выражения, помещенные в ячейках Таблицы (программный модуль при загрузке компилируется, а выражения в ячейках таблицы интерпретируются каждый раз при выводе секций).

Вариант 3

В программном модуле текст вывода данных в Таблицу следующий

Пока Запрос.Группировка("Товар") = 1 Цикл

   Тов = Запрос.Товар;

   ПечНаим = Тов.Наименование;

   ПечАртикул = Тов.Артикул;

   ПечЦена = Тов.Цена;

   ПечВалюта = Тов.Валюта;

   Таб.ВывестиСекцию("Товар1");

КонецЦикла;

Секция "Товар1" в Таблице имеет следующий формат:

<ПечНаим>

<ПечАртикул>

<ПсчЦена>

<ПечВалюта>

В третьем варианте отображаемые реквизиты товара вычисляются в программном модуле, а в ячейках Таблицы размещены простые выражения — ссылки на идентификаторы программного модуля. Доступ к каждому реквизиту товара происходит по сокращенному пути — через промежуточную переменную: Товар — Реквизит. В данном варианте фаза вывода данных в Таблицу работает быстрее, чем во втором варианте (выигрыш ~20%) и существено быстрее, чем в первом варианте (выигрыш ~40%), т. к. вычисление значений реквизитов объектов через «одну точку» выполняется быстрее, чем через «две (и более) точки».

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

·         не размещайте сложных выражений в ячейках Таблицы. Лучше вычислить необходимые значения непосредственно в программном модуле;

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







Перейти  к оглавлению: Описание встроенного языка