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