Как объединить две таблицы значений в 1С, включая данные из COM-соединения

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки в среде 1С:Предприятие часто возникает задача объединения данных из двух различных источников. Это может быть две таблицы значений (ТЗ), сформированные программно, или ситуация, когда одна таблица получена из текущей базы, а вторая — из внешней информационной базы через COMОбъект. Рассмотрим подробно, как правильно объединить такие таблицы, избежать распространенных ошибок и обеспечить высокую производительность кода.

Метод 1: Объединение таблиц через механизм запросов

Использование запроса для объединения двух таблиц значений является наиболее гибким методом. Он позволяет использовать мощные механизмы SQL-подобного языка 1С, такие как ПОЛНОЕ СОЕДИНЕНИЕ, группировки и функции агрегации. Однако, как мы видим из практики, простая передача таблиц в параметры запроса часто приводит к ошибкам.

Разберем по шагам, почему возникает ошибка и как ее исправить. Основная причина — отсутствие явной типизации колонок. Когда мы передаем ТаблицаЗначений в запрос, платформа должна знать типы данных каждой колонки, чтобы построить временную таблицу в СУБД.

Проанализируем правильный алгоритм подготовки таблиц:

  1. Создание типизированной таблицы: Если таблица создана динамически, необходимо использовать объект ОписаниеТипов.
  2. Заполнение данными: Данные копируются в новую структуру с четко заданными типами.
  3. Передача в запрос: Использование менеджера временных таблиц или прямой передачи параметров.

Посмотрим на пример кода, который решает проблему типизации:


// Подготовим описание типов для колонок
МассивТиповСтр = Новый Массив;
МассивТиповСтр.Добавить(Тип("Строка"));
ОписаниеТиповСтр = Новый ОписаниеТипов(МассивТиповСтр, , Новый КвалификаторыСтроки(100));

МассивТиповЧис = Новый Массив;
МассивТиповЧис.Добавить(Тип("Число"));
ОписаниеТиповЧис = Новый ОписаниеТипов(МассивТиповЧис, , Новый КвалификаторыЧисла(15, 3));

// Создаем типизированную таблицу
ТЗ_ДляЗапроса = Новый ТаблицаЗначений;
ТЗ_ДляЗапроса.Колонки.Добавить("Склад", ОписаниеТиповСтр);
ТЗ_ДляЗапроса.Колонки.Добавить("Номенклатура", ОписаниеТиповСтр);
ТЗ_ДляЗапроса.Колонки.Добавить("Количество", ОписаниеТиповЧис);

// Заполняем данными из исходной таблицы (например, полученной через COM)
Для Каждого СтрокаИзИсточника Из ИсходнаяТЗ Цикл
    ЗаполнитьЗначенияСвойств(ТЗ_ДляЗапроса.Добавить(), СтрокаИзИсточника);
КонецЦикла;

Метод 2: Особенности работы с данными из COM-объекта

Выясним причину, по которой данные из COMОбъект нельзя передавать в запрос напрямую. Объекты, полученные через COM-соединение, имеют тип COMОбъект, даже если визуально они выглядят как строки или числа. Запрос 1С не умеет работать с внешними типами данных внутри своего контекста.

Для корректной работы мы должны «приземлить» данные в текущую информационную базу. Проанализируем ситуацию: если вы получили таблицу ТЗ2 из внешней базы, вам нужно перебрать её строки и принудительно преобразовать значения к примитивным типам (строка, число, дата) или найти соответствующие ссылки в текущей базе по коду или артикулу.

Рассмотрим пример преобразования данных из COM:


// Предположим, ВнешняяТЗ получена через COM-соединение
ЛокальнаяТЗ = Новый ТаблицаЗначений;
ЛокальнаяТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ЛокальнаяТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));

Для Каждого ВнешняяСтрока Из ВнешняяТЗ Цикл
    НоваяСтрока = ЛокальнаяТЗ.Добавить();
    
    // Ищем номенклатуру в локальной базе по коду из внешней базы
    КодВнешний = СокрЛП(ВнешняяСтрока.НоменклатураКод);
    НайденнаяСсылка = Справочники.Номенклатура.НайтиПоКоду(КодВнешний);
    
    Если Не НайденнаяСсылка.Пустая() Тогда
        НоваяСтрока.Номенклатура = НайденнаяСсылка;
    КонецЕсли;
    
    НоваяСтрока.Количество = Число(ВнешняяСтрока.Количество); // Принудительное приведение типа
КонецЦикла;

Метод 3: Правильное написание запроса и обработка NULL

При использовании ПОЛНОЕ СОЕДИНЕНИЕ важно правильно обработать поля, по которым происходит связь. Если номенклатура есть в первой таблице, но отсутствует во второй, то в колонках второй таблицы будет значение 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);

РезультатЗапроса = Запрос.Выполнить().Выгрузить();

Метод 4: Использование индексов для больших таблиц

Если таблицы содержат десятки тысяч строк, запрос может работать медленно из-за объема передаваемых данных во временные таблицы. В таких случаях мы можем рассмотреть альтернативный вариант — программный обход с использованием индексов.

Проанализируем этот подход: мы берем одну таблицу за основу, а во второй ищем совпадения. Чтобы поиск работал мгновенно, необходимо добавить индекс по ключевому полю.

Пример программного объединения:


// Добавляем индекс для ускорения поиска
ТЗ2.Индексы.Добавить("Номенклатура");

// Создаем результирующую таблицу
Результат = ТЗ1.Скопировать();
Результат.Колонки.Добавить("КоличествоИзТЗ2", Новый ОписаниеТипов("Число"));

Для Каждого СтрокаРезульт Из Результат Цикл
    // Используем метод НайтиСтроки, он работает очень быстро по индексированным колонкам
    ПараметрыОтбора = Новый Структура("Номенклатура", СтрокаРезульт.Номенклатура);
    НайденныеСтроки = ТЗ2.НайтиСтроки(ПараметрыОтбора);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        СтрокаРезульт.КоличествоИзТЗ2 = НайденныеСтроки[0].Количество;
    КонецЕсли;
КонецЦикла;

Метод 5: Группировка и сворачивание дублей

Перед объединением или сразу после него часто требуется избавиться от дубликатов. Если в одной таблице склад и номенклатура встречаются дважды, прямое соединение создаст лишние строки (декартово произведение). Чтобы этого избежать, используем метод Свернуть().

Важный момент: метод Свернуть изменяет исходную таблицу, поэтому если она нужна в первоначальном виде, предварительно сделайте Скопировать().


// Группируем данные: первый параметр - колонки группировки, второй - суммируемые ресурсы
ТЗ1.Свернуть("Склад, Номенклатура", "Количество");
ТЗ2.Свернуть("Склад, Номенклатура", "Количество");

Подводя итог, можно сказать, что наиболее стабильным решением является предварительная очистка и типизация данных (особенно после COM), использование ЕСТЬNULL в запросах для объединения полей и обязательная группировка данных методом Свернуть во избежание дублирования строк.

← На главную