При разработке на платформе 1С:Предприятие одной из самых частых задач является получение контактной информации объектов (телефонов, адресов электронной почты, фактических адресов). Начинающие разработчики нередко совершают типичную ошибку: для каждого типа контактной информации они создают и выполняют отдельный запрос к базе данных. Это приводит к избыточной нагрузке на СУБД, замедлению работы интерфейса и усложнению программного кода.
В этой статье мы подробно разберем, как объединить два подобных запроса в один, рассмотрим различные варианты решения этой задачи — от классического использования параметров-массивов до современных стандартов разработки с применением функций Библиотеки стандартных подсистем (БСП) и Системы компоновки данных (СКД).
Рассмотрим ситуацию, с которой столкнулся автор темы на форуме. Ему необходимо получить номера телефонов и адреса электронной почты для определенного объекта, а затем вывести их в виде текстовых строк, где значения разделены запятыми. Исходный код содержал два практически одинаковых запроса к регистру сведений РегистрСведений.КонтактнаяИнформация, которые отличались только передаваемым параметром &Тип (в первом случае — телефон, во втором — адрес электронной почты).
Выполнение двух последовательных запросов в цикле или даже просто подряд в одном контексте — это неоптимальный подход. Каждый запрос тратит время на установку соединения с СУБД, компиляцию плана запроса и передачу данных по сети. Наша цель — получить все необходимые данные за один запрос и затем эффективно распределить их в коде встроенного языка.
Самый простой и логичный способ объединить такие запросы — использовать оператор В в условии ГДЕ языка запросов 1С и передать туда массив, содержащий все интересующие нас типы контактной информации. Вместо последовательного выполнения запросов мы сразу указываем системе, что нам нужны и телефоны, и почта.
Давайте разберем по шагам, как написать такой запрос и как правильно обработать его результаты с использованием современных возможностей платформы, таких как функция СтрСоединить(). Это избавит нас от необходимости вручную обрезать лишние запятые в начале или конце строки с помощью функции Сред().
Проанализируем оптимизированный вариант программного кода:
// 1. Подготовка параметров запроса
МассивТипов = Новый Массив;
МассивТипов.Добавить(Перечисления.ТипыКонтактнойИнформации.Телефон);
МассивТипов.Добавить(Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты);
// 2. Создание и инициализация запроса
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| КонтактнаяИнформация.Представление КАК Представление,
| КонтактнаяИнформация.Тип КАК Тип
|ИЗ
| РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
|ГДЕ
| КонтактнаяИнформация.Объект = &Объект
| И КонтактнаяИнформация.Тип В (&МассивТипов)
|УПОРЯДОЧИТЬ ПО
| КонтактнаяИнформация.ЗначениеПоУмолчанию УБЫВ";
Запрос.УстановитьПараметр("Объект", ЭтотОбъект.Ссылка);
Запрос.УстановитьПараметр("МассивТипов", МассивТипов);
// 3. Выполнение запроса
Выборка = Запрос.Выполнить().Выбрать();
// 4. Распределение результатов по временным массивам
МассивТелефонов = Новый Массив;
МассивПочты = Новый Массив;
Пока Выборка.Следующий() Цикл
Если Выборка.Тип = Перечисления.ТипыКонтактнойИнформации.Телефон Тогда
МассивТелефонов.Добавить(Выборка.Представление);
ИначеЕсли Выборка.Тип = Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты Тогда
МассивПочты.Добавить(Выборка.Представление);
КонецЕсли;
КонецЦикла;
// 5. Быстрое и чистое объединение массивов строк через разделитель
ЗначениеСтароеТелефона = СтрСоединить(МассивТелефонов, ", ");
ЗначениеСтароеПочта = СтрСоединить(МассивПочты, ", ");
В чем преимущества этого подхода?
МассивТипов и дописать одно условие ИначеЕсли внутри цикла обхода выборки.В сообщении №3 на форуме было предложено использовать конструкцию ОБЪЕДИНИТЬ для разделения полей в запросе на отдельные колонки. Рассмотрим этот вариант подробнее, так как здесь кроется очень важный нюанс, влияющий на производительность всей системы.
Давайте выясним разницу между операторами ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ. С точки зрения стандартов разработки фирмы "1С", использование оператора ОБЪЕДИНИТЬ (без слова ВСЕ) в большинстве случаев является грубой ошибкой проектирования.
Когда СУБД выполняет инструкцию ОБЪЕДИНИТЬ (аналог UNION в SQL), она вынуждена произвести дополнительную операцию сравнения всех строк результирующего набора данных, чтобы исключить возможные дубликаты. Это требует значительных ресурсов процессора и памяти, особенно при работе с большими объемами данных. Оператор ОБЪЕДИНИТЬ ВСЕ (аналог UNION ALL в SQL) просто склеивает результаты двух выборок по вертикали без проверки на уникальность, что работает в разы быстрее.
Посмотрим на пример правильного использования объединения таблиц в запросе:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| КонтактнаяИнформация.Представление КАК ПредставлениеТелефон,
| """" КАК ПредставлениеЕмайл
|ИЗ
| РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
|ГДЕ
| КонтактнаяИнформация.Объект = &Объект
| И КонтактнаяИнформация.Тип = &ТипТелефон
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| """",
| КонтактнаяИнформация.Представление
|ИЗ
| РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
|ГДЕ
| КонтактнаяИнформация.Объект = &Объект
| И КонтактнаяИнформация.Тип = &ТипЕмайл";
Запрос.УстановитьПараметр("Объект", ЭтотОбъект.Ссылка);
Запрос.УстановитьПараметр("ТипТелефон", Перечисления.ТипыКонтактнойИнформации.Телефон);
Запрос.УстановитьПараметр("ТипЕмайл", Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты);
Выборка = Запрос.Выполнить().Выбрать();
Хотя этот способ является рабочим, в данной конкретной задаче он уступает первому решению по изящности кода. Нам все равно придется обходить выборку в цикле и отсеивать пустые строки, поэтому первый вариант с использованием массива параметров является более предпочтительным и компактным.
Если вы работаете в современной типовой конфигурации (например, 1С:ERP, 1С:Управление торговлей 11, 1С:Комплексная автоматизация 2, 1С:Бухгалтерия предприятия 3.0), то прямой запрос к регистру сведений РегистрСведений.КонтактнаяИнформация считается устаревшим подходом (deprecated).
В актуальных версиях программных продуктов 1С, использующих Библиотеку стандартных подсистем (БСП), контактная информация объектов может храниться не в отдельном регистре, а непосредственно в табличных частях самих справочников (например, табличная часть КонтактнаяИнформация у справочника Контрагенты). Прямое обращение к старому регистру в таких базах вернет пустой результат или приведет к ошибке при обновлении конфигурации.
Для получения контактной информации разработчики БСП предусмотрели готовый программный интерфейс — общий модуль УправлениеКонтактнойИнформацией. Давайте рассмотрим, как решить задачу автора всего в несколько строк кода вообще без написания ручных SQL-запросов.
Разберем пример получения контактной информации через БСП:
// Получаем таблицу всей контактной информации по конкретному объекту
ТаблицаКИ = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъектов(Новый Массив(ЭтотОбъект.Ссылка));
// Фильтруем таблицу или ищем нужные виды КИ через встроенные функции поиска БСП
ПараметрыОтбора = Новый Структура;
// Для получения телефона
ПараметрыОтбора.Вставить("Тип", Перечисления.ТипыКонтактнойИнформации.Телефон);
СтрокиТелефонов = ТаблицаКИ.НайтиСтроки(ПараметрыОтбора);
МассивТелефонов = Новый Массив;
Для Каждого СтрокаТлф Из СтрокиТелефонов Цикл
МассивТелефонов.Добавить(СтрокаТлф.Представление);
КонецЦикла;
ЗначениеСтароеТелефона = СтрСоединить(МассивТелефонов, ", ");
// Для получения почты
ПараметрыОтбора.Вставить("Тип", Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты);
СтрокиПочты = ТаблицаКИ.НайтиСтроки(ПараметрыОтбора);
МассивПочты = Новый Массив;
Для Каждого СтрокаПочты Из СтрокиПочты Цикл
МассивПочты.Добавить(СтрокаПочты.Представление);
КонецЦикла;
ЗначениеСтароеПочта = СтрСоединить(МассивПочты, ", ");
Преимущества использования БСП:
Если перед вами стоит задача вывести объединенные через запятую контактные данные не в программном модуле для проведения документов, а в отчете, то писать код на встроенном языке вообще не требуется. С этой задачей великолепно справляется встроенный механизм Системы компоновки данных (СКД).
В обычном языке запросов 1С отсутствует прямая агрегатная функция склеивания строк из разных записей (такая как STRING_AGG в MS SQL Server или GROUP_CONCAT в MySQL). Однако в СКД есть специализированная функция ресурса СоединитьСтроки().
Для реализации этого механизма выполните следующие шаги:
Представление, Тип и Объект из контактной информации.Представление в список ресурсов.СоединитьСтроки(ВычислитьВыражениеТаблица("Представление"), ", ")Объект.Система компоновки данных автоматически сгруппирует все записи по каждому контрагенту или партнеру и соединит их телефоны или адреса электронной почты в одну красивую строку через запятую без написания единой строчки программного кода.
Подводя итоги, мы можем сделать следующие выводы по оптимизации работы с запросами в 1С:
В.ОБЪЕДИНИТЬ ВСЕ вместо ОБЪЕДИНИТЬ для сохранения высокой производительности базы данных.СтрСоединить() для склеивания массивов строк с разделителями вместо устаревших конструкций с циклами и функцией Сред().УправлениеКонтактнойИнформацией из состава БСП — это гарантирует стабильность работы программы при обновлениях конфигурации.