Как объединить два запроса к контактной информации в один в 1С и оптимизировать обработку данных

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

При разработке на платформе 1С:Предприятие одной из самых частых задач является получение контактной информации объектов (телефонов, адресов электронной почты, фактических адресов). Начинающие разработчики нередко совершают типичную ошибку: для каждого типа контактной информации они создают и выполняют отдельный запрос к базе данных. Это приводит к избыточной нагрузке на СУБД, замедлению работы интерфейса и усложнению программного кода.

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

Анализ исходной проблемы

Рассмотрим ситуацию, с которой столкнулся автор темы на форуме. Ему необходимо получить номера телефонов и адреса электронной почты для определенного объекта, а затем вывести их в виде текстовых строк, где значения разделены запятыми. Исходный код содержал два практически одинаковых запроса к регистру сведений РегистрСведений.КонтактнаяИнформация, которые отличались только передаваемым параметром &Тип (в первом случае — телефон, во втором — адрес электронной почты).

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

Решение 1. Использование массива параметров и оператора "В" (Рекомендуемый классический метод)

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

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

Проанализируем оптимизированный вариант программного кода:


// 1. Подготовка параметров запроса
МассивТипов = Новый Массив;
МассивТипов.Добавить(Перечисления.ТипыКонтактнойИнформации.Телефон);
МассивТипов.Добавить(Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты);

// 2. Создание и инициализация запроса
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	КонтактнаяИнформация.Представление КАК Представление,
    |	КонтактнаяИнформация.Тип КАК Тип
    |ИЗ
    |	РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
    |ГДЕ
    |	КонтактнаяИнформация.Объект = &Объект
    |	И КонтактнаяИнформация.Тип В (&МассивТипов)
    |УПОРЯДОЧИТЬ ПО
    |	КонтактнаяИнформация.ЗначениеПоУмолчанию УБЫВ";

Запрос.УстановитьПараметр("Объект", ЭтотОбъект.Ссылка);
Запрос.УстановитьПараметр("МассивТипов", МассивТипов);

// 3. Выполнение запроса
Выборка = Запрос.Выполнить().Выбрать();

// 4. Распределение результатов по временным массивам
МассивТелефонов = Новый Массив;
МассивПочты = Новый Массив;

Пока Выборка.Следующий() Цикл
    Если Выборка.Тип = Перечисления.ТипыКонтактнойИнформации.Телефон Тогда
        МассивТелефонов.Добавить(Выборка.Представление);
    ИначеЕсли Выборка.Тип = Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты Тогда
        МассивПочты.Добавить(Выборка.Представление);
    КонецЕсли;
КонецЦикла;

// 5. Быстрое и чистое объединение массивов строк через разделитель
ЗначениеСтароеТелефона = СтрСоединить(МассивТелефонов, ", ");
ЗначениеСтароеПочта = СтрСоединить(МассивПочты, ", ");

В чем преимущества этого подхода?

  1. Один запрос к базе данных. СУБД мгновенно выбирает все записи, соответствующие обоим типам информации, за один проход по индексу.
  2. Использование СтрСоединить(). Данный метод встроенного языка (доступен начиная с версии платформы 8.3.6) избавляет нас от ручного удаления лишних символов разметки (запятых, пробелов) в цикле. Код становится чище, проще для чтения и работает быстрее.
  3. Гибкость. Если в будущем вам потребуется добавить еще один тип контактной информации (например, фактический адрес), вам достаточно будет просто добавить новый элемент в МассивТипов и дописать одно условие ИначеЕсли внутри цикла обхода выборки.

Решение 2. Использование конструкции ОБЪЕДИНИТЬ ВСЕ

В сообщении №3 на форуме было предложено использовать конструкцию ОБЪЕДИНИТЬ для разделения полей в запросе на отдельные колонки. Рассмотрим этот вариант подробнее, так как здесь кроется очень важный нюанс, влияющий на производительность всей системы.

Давайте выясним разницу между операторами ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ. С точки зрения стандартов разработки фирмы "1С", использование оператора ОБЪЕДИНИТЬ (без слова ВСЕ) в большинстве случаев является грубой ошибкой проектирования.

Когда СУБД выполняет инструкцию ОБЪЕДИНИТЬ (аналог UNION в SQL), она вынуждена произвести дополнительную операцию сравнения всех строк результирующего набора данных, чтобы исключить возможные дубликаты. Это требует значительных ресурсов процессора и памяти, особенно при работе с большими объемами данных. Оператор ОБЪЕДИНИТЬ ВСЕ (аналог UNION ALL в SQL) просто склеивает результаты двух выборок по вертикали без проверки на уникальность, что работает в разы быстрее.

Посмотрим на пример правильного использования объединения таблиц в запросе:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	КонтактнаяИнформация.Представление КАК ПредставлениеТелефон,
    |	"""" КАК ПредставлениеЕмайл
    |ИЗ
    |	РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
    |ГДЕ
    |	КонтактнаяИнформация.Объект = &Объект
    |	И КонтактнаяИнформация.Тип = &ТипТелефон
    |
    |ОБЪЕДИНИТЬ ВСЕ
    |
    |ВЫБРАТЬ
    |	"""",
    |	КонтактнаяИнформация.Представление
    |ИЗ
    |	РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
    |ГДЕ
    |	КонтактнаяИнформация.Объект = &Объект
    |	И КонтактнаяИнформация.Тип = &ТипЕмайл";

Запрос.УстановитьПараметр("Объект", ЭтотОбъект.Ссылка);
Запрос.УстановитьПараметр("ТипТелефон", Перечисления.ТипыКонтактнойИнформации.Телефон);
Запрос.УстановитьПараметр("ТипЕмайл", Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты);

Выборка = Запрос.Выполнить().Выбрать();

Хотя этот способ является рабочим, в данной конкретной задаче он уступает первому решению по изящности кода. Нам все равно придется обходить выборку в цикле и отсеивать пустые строки, поэтому первый вариант с использованием массива параметров является более предпочтительным и компактным.

Решение 3. Современный стандарт разработки — использование механизмов БСП

Если вы работаете в современной типовой конфигурации (например, 1С:ERP, 1С:Управление торговлей 11, 1С:Комплексная автоматизация 2, 1С:Бухгалтерия предприятия 3.0), то прямой запрос к регистру сведений РегистрСведений.КонтактнаяИнформация считается устаревшим подходом (deprecated).

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

Для получения контактной информации разработчики БСП предусмотрели готовый программный интерфейс — общий модуль УправлениеКонтактнойИнформацией. Давайте рассмотрим, как решить задачу автора всего в несколько строк кода вообще без написания ручных SQL-запросов.

Разберем пример получения контактной информации через БСП:


// Получаем таблицу всей контактной информации по конкретному объекту
ТаблицаКИ = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъектов(Новый Массив(ЭтотОбъект.Ссылка));

// Фильтруем таблицу или ищем нужные виды КИ через встроенные функции поиска БСП
ПараметрыОтбора = Новый Структура;

// Для получения телефона
ПараметрыОтбора.Вставить("Тип", Перечисления.ТипыКонтактнойИнформации.Телефон);
СтрокиТелефонов = ТаблицаКИ.НайтиСтроки(ПараметрыОтбора);
МассивТелефонов = Новый Массив;
Для Каждого СтрокаТлф Из СтрокиТелефонов Цикл
    МассивТелефонов.Добавить(СтрокаТлф.Представление);
КонецЦикла;
ЗначениеСтароеТелефона = СтрСоединить(МассивТелефонов, ", ");

// Для получения почты
ПараметрыОтбора.Вставить("Тип", Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты);
СтрокиПочты = ТаблицаКИ.НайтиСтроки(ПараметрыОтбора);
МассивПочты = Новый Массив;
Для Каждого СтрокаПочты Из СтрокиПочты Цикл
    МассивПочты.Добавить(СтрокаПочты.Представление);
КонецЦикла;
ЗначениеСтароеПочта = СтрСоединить(МассивПочты, ", ");

Преимущества использования БСП:

Решение 4. Решение задачи в отчетах без кода (через СКД)

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

В обычном языке запросов 1С отсутствует прямая агрегатная функция склеивания строк из разных записей (такая как STRING_AGG в MS SQL Server или GROUP_CONCAT в MySQL). Однако в СКД есть специализированная функция ресурса СоединитьСтроки().

Для реализации этого механизма выполните следующие шаги:

  1. Создайте схему компоновки данных и добавьте простой запрос, выбирающий Представление, Тип и Объект из контактной информации.
  2. Перейдите на вкладку Ресурсы вашего набора данных.
  3. Добавьте поле Представление в список ресурсов.
  4. В поле Выражение напишите формулу:
    СоединитьСтроки(ВычислитьВыражениеТаблица("Представление"), ", ")
  5. В поле Рассчитывать по... укажите группировку Объект.

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

Резюме

Подводя итоги, мы можем сделать следующие выводы по оптимизации работы с запросами в 1С:

← На главную