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