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