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