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