Как выполнить выборку данных из журнала документов во внешней обработке 1С, передавая параметры с формы?

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Представим ситуацию: нам необходимо создать внешнюю обработку, которая будет выбирать документы из журнала под названием "Складские документы", фильтруя их по определенному контрагенту и складу. Эти параметры (Контрагент и Склад) должны задаваться пользователем непосредственно на форме внешней обработки.

Шаг 1: Подготовка внешней обработки и формы

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

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


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

На данном этапе новички часто сталкиваются с проблемой: как правильно передать значения реквизитов формы в параметры запроса? Если просто написать Запрос.УстановитьПараметр("Контрагент", Контрагент);, платформа выдаст ошибку, поскольку в контексте обработчика команды Контрагент (как имя переменной) не определен.

Шаг 2: Правильная передача параметров запроса с формы

Разберем причину ошибки и найдем верное решение. В управляемых формах 1С для доступа к реквизитам, расположенным на форме, из клиентского или серверного кода используется специальный объект Объект. Этот объект является представлением данных формы и содержит все ее реквизиты. Таким образом, чтобы передать значение реквизита Контрагент с формы в параметр запроса, нам необходимо обратиться к нему как Объект.Контрагент.

Применим это правило к нашему запросу:


&НаКлиенте
Процедура Показать(Команда)
    // Передаем команду на сервер для выполнения запроса
    ВыполнитьЗапросНаСервере();
КонецПроцедуры

&НаСервере
Процедура ВыполнитьЗапросНаСервере()
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ 
    |    СкладскиеДокументы.Дата КАК Дата, 
    |    СкладскиеДокументы.Контрагент КАК Контрагент, 
    |    СкладскиеДокументы.Склад КАК Склад, 
    |    СкладскиеДокументы.Заказ КАК Заказ 
    |ПОМЕСТИТЬ ВыборДокументов 
    |ИЗ 
    |    ЖурналДокументов.СкладскиеДокументы КАК СкладскиеДокументы 
    |ГДЕ 
    |    СкладскиеДокументы.Контрагент = &Контрагент 
    |И    СкладскиеДокументы.Склад = &Склад";
    
    // **********************************************
    // Верное решение для установки параметров запроса:
    Запрос.УстановитьПараметр("Контрагент", Объект.Контрагент); 
    Запрос.УстановитьПараметр("Склад", Объект.Склад);
    // **********************************************

    РезультатЗапроса = Запрос.Выполнить();
    // ... далее код вывода результатов
КонецПроцедуры

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

Шаг 3: Корректный вывод результатов запроса

После того как запрос успешно выполнен, перед нами встает новая задача: как правильно отобразить полученные данные. Простая команда Сообщить(РезультатЗапроса) не даст нам желаемого результата – она выведет лишь объект РезультатЗапроса, а не содержимое выборки. Для просмотра фактических данных нам нужно либо обойти выборку построчно, либо выгрузить ее в табличную часть формы.

Вариант 1: Построчный обход выборки

Этот метод подходит, если нам нужно обработать каждую строку результата запроса по отдельности, выполнить какие-либо вычисления или вывести данные в виде текстовых сообщений. Для этого используем метод Выбрать() объекта РезультатЗапроса и цикл Пока ... Цикл:


&НаСервере
Процедура ВыполнитьЗапросНаСервере()
    // ... (код запроса и установки параметров) ...
    
    РезультатЗапроса = Запрос.Выполнить();
    
    // Получаем выборку из результата запроса
    Выборка = РезультатЗапроса.Выбрать(); 
    
    Сообщить("--- Результаты выборки: ---");
    Пока Выборка.Следующий() Цикл
        // Обрабатываем каждую строку
        Сообщить(Строка(Выборка.Дата) + " | Контрагент: " + Выборка.Контрагент + " | Склад: " + Выборка.Склад + " | Заказ: " + Выборка.Заказ);
    КонецЦикла;
    Сообщить("-------------------------");
КонецПроцедуры

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

Вариант 2: Вывод в табличную часть формы

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

Далее, мы используем метод Выгрузить() объекта РезультатЗапроса, чтобы получить ТаблицуЗначений, и затем загружаем ее в нашу табличную часть формы:


&НаСервере
Процедура ВыполнитьЗапросНаСервере()
    // ... (код запроса и установки параметров) ...
    
    РезультатЗапроса = Запрос.Выполнить();
    
    // Выгружаем результат запроса в ТаблицуЗначений
    ТаблицаРезультатов = РезультатЗапроса.Выгрузить();
    
    // Очищаем существующую табличную часть формы
    Объект.ТабЧастьРезультатов.Очистить(); 
    
    // Загружаем данные в табличную часть формы
    Объект.ТабЧастьРезультатов.Загрузить(ТаблицаРезультатов);
КонецПроцедуры

Важный нюанс: при использовании метода Загрузить() для табличной части, очень желательно, чтобы имена колонок в результате запроса (Дата, Контрагент, Склад, Заказ) совпадали с именами колонок табличной части Объект.ТабЧастьРезультатов. Если имена колонок не совпадают, данные могут быть загружены некорректно или вообще не отобразиться. В таком случае вам придется вручную переносить данные из ТаблицыЗначений в табличную часть формы, создавая новые строки и присваивая значения полям.

Шаг 4: Особенности работы с временными таблицами и пакетными запросами

В нашем исходном запросе мы использовали конструкцию ПОМЕСТИТЬ ВыборДокументов, что означает создание временной таблицы. Временные таблицы очень полезны для разбиения сложных запросов на несколько более простых шагов, что улучшает читаемость и иногда производительность. Однако есть свои особенности при их использовании.

Если запрос содержит одну или несколько временных таблиц, а затем основной запрос, который использует эти временные таблицы, мы работаем с пакетными запросами. Для выполнения пакета запросов используются методы:

  1. Запрос.ВыполнитьПакет(): Возвращает массив объектов РезультатЗапроса, по одному для каждого запроса в пакете. При этом результаты временных таблиц не детализируются, вы получаете только результат последнего запроса.
  2. Запрос.ВыполнитьПакетСПромежуточнымиДанными(): Этот метод также возвращает массив объектов РезультатЗапроса, но позволяет получить доступ к содержимому временных таблиц.

Если вы хотите просмотреть содержимое временной таблицы во время отладки или использовать его для дальнейшей обработки, то ВыполнитьПакетСПромежуточнымиДанными() будет предпочтительнее. Каждый элемент массива будет соответствовать порядку запросов в пакете.

Пример использования ВыполнитьПакетСПромежуточнымиДанными() для получения данных из временной таблицы:


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

    Запрос.УстановитьПараметр("Контрагент", Объект.Контрагент); 
    Запрос.УстановитьПараметр("Склад", Объект.Склад);
    Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ТекущаяДата())); // Пример параметра

    МассивРезультатов = Запрос.ВыполнитьПакетСПромежуточнымиДанными();
    
    // Результат первого запроса (временная таблица ВТ_ВыбранныеДокументы)
    РезультатВременнойТаблицы = МассивРезультатов[0];
    Сообщить("Количество записей во временной таблице: " + РезультатВременнойТаблицы.Количество());
    // Если нужно выгрузить и просмотреть:
    // ТаблицаВТ = РезультатВременнойТаблицы.Выгрузить();
    // Сообщить(ТаблицаВТ.Количество());

    // Результат второго (основного) запроса
    РезультатОсновногоЗапроса = МассивРезультатов[1];
    
    Объект.ТабЧастьРезультатов.Очистить(); 
    Объект.ТабЧастьРезультатов.Загрузить(РезультатОсновногоЗапроса.Выгрузить());
КонецПроцедуры

Если же временная таблица нужна только для структурирования запроса и ее содержимое не требуется отдельно, вы можете не использовать `ПОМЕСТИТЬ` или выполнять `Запрос.Выполнить()`, если это единственный запрос в тексте.

Шаг 5: Дополнительные аспекты и советы по работе с запросами

  1. Отладка запросов:
    • Для эффективной отладки запросов и анализа их производительности рекомендуем использовать "Консоль запросов". Это внешняя обработка, позволяющая выполнять запросы на реальных данных, задавать параметры, просматривать содержимое временных таблиц и замерять время выполнения. Это незаменимый инструмент для любого разработчика 1С.
    • При отладке внешней обработки, особенно в клиент-серверном варианте, убедитесь, что ваш код выполняется на правильной стороне (клиент или сервер). Запросы к базе данных всегда выполняются на сервере, поэтому их необходимо обертывать в серверные процедуры (как мы сделали в примере с &НаСервере).
    • С версии платформы 8.3.8 для отладки временных таблиц можно использовать Запрос.МенеджерВременныхТаблиц.Таблицы[0].ПолучитьДанные().Выгрузить(), чтобы получить данные первой временной таблицы в виде таблицы значений без выполнения всего пакета запросов, что удобно при поэтапной отладке.
  2. Обработка ошибок:
    • Всегда предусматривайте механизмы обработки ошибок с помощью конструкции Попытка ... Исключение ... КонецПопытки. Это позволит перехватывать ошибки, возникающие при выполнении запросов (например, из-за неверных параметров или отсутствия прав), и выводить пользователю более понятное сообщение, а не стандартное системное.
    • Начиная с версии платформы 8.3.17, доступны расширенные возможности по обработке ошибок, включая обработчик модуля приложения ОбработкаОтображенияОшибки и объект ОбработкаОшибок для более гибкой настройки вывода сообщений и сбора отчетов об ошибках.
  3. Оптимизация запросов:
    • Грамотно построенные запросы напрямую влияют на скорость работы системы. Избегайте избыточных обращений к базе данных, например, не делайте циклы с запросами внутри, если можно получить данные одним запросом.
    • Использование индексов (которые обычно создаются автоматически платформой для стандартных полей и измерений) и правильное составление условий ГДЕ может существенно ускорить выборку данных. Если запрос работает медленно, проанализируйте его в консоли запросов.
    • Старайтесь указывать точные типы для параметров запроса, когда это возможно, чтобы платформа могла более эффективно использовать индексы.

Надеемся, что это подробное руководство помогло вам разобраться в тонкостях работы с запросами во внешних обработках 1С. Практикуйтесь, экспериментируйте с кодом, и вы быстро освоите эти мощные инструменты платформы.

← На главную