При разработке печатных форм и сложных отчетов в системе «1С:Предприятие» программисты часто используют пакетные запросы. Это позволяет за одно обращение к базе данных получить всю необходимую информацию, что положительно сказывается на производительности. Однако при работе с пакетами запросов, содержащими временные таблицы, часто возникают трудности с получением промежуточных результатов в коде. Другая распространенная проблема в современных конфигурациях (таких как 1С:ERP, 1С:Комплексная автоматизация 2 или 1С:Управление торговлей 11) связана с разделением сущностей Партнеры и Контрагенты при поиске контактных лиц.
В данной статье мы подробно разберем, почему не удается напрямую выбрать данные из первого пакета запроса, содержащего конструкцию ПОМЕСТИТЬ, рассмотрим альтернативные методы работы с пакетами и выясним, как правильно связать контрагентов с их контактными лицами.
Рассмотрим классическую ситуацию. Разработчик пишет пакетный запрос, где в первом пакете выбираются контактные лица и помещаются во временную таблицу, а во втором — выбирается табличная часть документа:
Запрос = Новый Запрос;
Запрос.Текст = "
|ВЫБРАТЬ
| КонтактныеЛицаПартнеров.Наименование КАК КонтактноеЛицоИсполнителя
|ПОМЕСТИТЬ ВТ_КонтактноеЛицоИсполнителя
|ИЗ
| Справочник.КонтактныеЛицаПартнеров КАК КонтактныеЛицаПартнеров
|ГДЕ
| КонтактныеЛицаПартнеров.Владелец = &Контрагент
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ПоступлениеУслугПрочихАктивовРасходы.НомерСтроки КАК ПечНомПП,
| ПоступлениеУслугПрочихАктивовРасходы.Содержание КАК ПечУслуга
|ИЗ
| Документ.ПоступлениеУслугПрочихАктивов.Расходы КАК ПоступлениеУслугПрочихАктивовРасходы
|ГДЕ
| ПоступлениеУслугПрочихАктивовРасходы.Ссылка = &Ссылка";
При попытке выполнить код получения данных первого пакета:
МассивРезультатов = Запрос.ВыполнитьПакет();
ВыборкаКЛИсполнителя = МассивРезультатов[0].Выбрать();
Если ВыборкаКЛИсполнителя.Следующий() Тогда
// Мы ожидаем попасть сюда, но этого не происходит!
КонецЕсли;
Выясним причину такого поведения платформы. Метод ВыполнитьПакет() возвращает массив результатов для каждого запроса в пакете. Однако, если запрос содержит инструкцию ПОМЕСТИТЬ, результатом этого шага в массиве будет таблица, состоящая всего из одной строки и одной колонки с именем «Количество». В этой ячейке СУБД возвращает число записей, которые были записаны во временную таблицу. Сами данные строк методом ВыполнитьПакет() из временной таблицы получить невозможно.
Проанализируем три правильных способа решения этой технической задачи.
Если данные первого пакета запроса не используются во втором пакете для связи, объединения или вложенных условий, то временная таблица попросту не нужна. Мы можем сделать первый пакет обычным выбором данных (без конструкции ПОМЕСТИТЬ). Тогда метод ВыполнитьПакет() вернет массив полноценных таблиц значений с реальными строками.
Рассмотрим, как будет выглядеть исправленный код:
Запрос = Новый Запрос;
Запрос.Текст = "
|ВЫБРАТЬ
| КонтактныеЛицаПартнеров.Наименование КАК КонтактноеЛицоИсполнителя
|ИЗ
| Справочник.КонтактныеЛицаПартнеров КАК КонтактныеЛицаПартнеров
|ГДЕ
| КонтактныеЛицаПартнеров.Владелец = &Контрагент
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ПоступлениеУслугПрочихАктивовРасходы.НомерСтроки КАК ПечНомПП,
| ПоступлениеУслугПрочихАктивовРасходы.Содержание КАК ПечУслуга
|ИЗ
| Документ.ПоступлениеУслугПрочихАктивов.Расходы КАК ПоступлениеУслугПрочихАктивовРасходы
|ГДЕ
| ПоступлениеУслугПрочихАктивовРасходы.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ПоступлениеУслуг.Ссылка);
Запрос.УстановитьПараметр("Контрагент", ПоступлениеУслуг.Контрагент);
МассивРезультатов = Запрос.ВыполнитьПакет();
// Теперь первый элемент массива содержит реальную выборку контактных лиц
ВыборкаКЛИсполнителя = МассивРезультатов[0].Выбрать();
Если ВыборкаКЛИсполнителя.Следующий() Тогда
Подвал.Параметры.ПечРасшифровкаИсп = ВыборкаКЛИсполнителя.КонтактноеЛицоИсполнителя;
КонецЕсли;
Если временная таблица все же необходима для логики последующих пакетов запроса, но ее данные также нужно прочитать в коде 1С, мы можем использовать альтернативный метод платформы — ВыполнитьПакетСПромежуточнымиДанными().
Этот метод работает аналогично ВыполнитьПакет(), но для шагов с конструкцией ПОМЕСТИТЬ он возвращает в массив не число записей, а полноценную таблицу с данными, которые попали во временную таблицу.
Важное предупреждение по производительности: фирма «1С» рекомендует использовать метод ВыполнитьПакетСПромежуточнымиДанными() преимущественно для отладки. Его постоянное применение в высоконагруженном рабочем коде нежелательно, так как сервер «1С:Предприятия» тратит дополнительные ресурсы на передачу промежуточных временных таблиц из СУБД обратно на сервер приложений.
Наиболее профессиональным и производительным способом работы с временными таблицами, данные которых нужно проанализировать в коде, является использование объекта МенеджерВременныхТаблиц. Посмотрим на пример реализации данного подхода:
МВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МВТ;
Запрос.Текст = "
|ВЫБРАТЬ
| КонтактныеЛицаПартнеров.Наименование КАК КонтактноеЛицоИсполнителя
|ПОМЕСТИТЬ ВТ_КонтактноеЛицоИсполнителя
|ИЗ
| Справочник.КонтактныеЛицаПартнеров КАК КонтактныеЛицаПартнеров
|ГДЕ
| КонтактныеЛицаПартнеров.Владелец = &Контрагент";
Запрос.УстановитьПараметр("Контрагент", ПоступлениеУслуг.Контрагент);
Запрос.Выполнить(); // Выполняем запрос, таблица создана в МВТ
// Теперь мы можем получить данные напрямую из менеджера временных таблиц
ТаблицаКонтактныхЛиц = МВТ.Таблицы.Найти("ВТ_КонтактноеЛицоИсполнителя").ПолучитьДанные().Выгрузить();
Если ТаблицаКонтактныхЛиц.Количество() > 0 Тогда
Подвал.Параметры.ПечРасшифровкаИсп = ТаблицаКонтактныхЛиц[0].КонтактноеЛицоИсполнителя;
КонецЕсли;
Даже после исправления технической стороны запроса разработчики часто сталкиваются с тем, что выборка контактных лиц все равно остается пустой. Проанализируем причину этой логической нестыковки, которая кроется в архитектуре современных конфигураций на базе БСП (ERP, КА, УТ).
В этих системах справочники разделены на две важные сущности:
Справочник КонтактныеЛицаПартнеров подчинен именно справочнику Партнеры. Реквизит Владелец в нем имеет тип СправочникСсылка.Партнеры. Если мы передаем в параметр &Контрагент ссылку на справочник Контрагенты, система пытается сравнить разнородные типы данных, и база данных возвращает пустой результат.
Для корректного отбора в запросе нам необходимо обратиться к реквизиту Партнер, который хранится в карточке контрагента. Разберем правильное условие связи в запросе:
|ГДЕ
| КонтактныеЛицаПартнеров.Владелец = &Контрагент.Партнер
В этом случае платформа автоматически выполнит левое соединение с таблицей контрагентов и подставит в условие сравнения правильного владельца-партнера.
Если в вашей конфигурации внедрена Библиотека стандартных подсистем (БСП), то писать сложные запросы для получения контактных лиц по умолчанию не требуется. Мы можем воспользоваться готовыми экспортными методами из общего модуля ПартнерыИКонтрагенты. Рассмотрим пример:
// Получаем основное контактное лицо контрагента программно
КонтактноеЛицо = ПартнерыИКонтрагенты.ПолучитьКонтактноеЛицоПартнераКонтрагентаПоУмолчанию(ПоступлениеУслуг.Контрагент);
Если ЗначениеЗаполнено(КонтактноеЛицо) Тогда
Подвал.Параметры.ПечРасшифровкаИсп = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(КонтактноеЛицо, "Наименование");
КонецЕсли;
Использование таких функций БСП делает код более читаемым, снижает вероятность ошибок при обновлении конфигурации и избавляет от необходимости ручного написания пакетных запросов для простых печатных форм.