Как соединить результаты нескольких запросов через Менеджер временных таблиц в 1С

Программист IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда данные для итогового отчета или обработки нужно собрать из разных источников. Иногда это два разных запроса к базе данных, а иногда — сочетание данных из базы и внешней таблицы значений, сформированной программно. Начинающие разработчики часто пытаются «сложить» объекты типа ВыборкаИзРезультатаЗапроса напрямую, но сталкиваются с тем, что у этих объектов нет методов для слияния.

В этой статье мы подробно разберем, почему нельзя соединить выборки напрямую, и как правильно использовать объект МенеджерВременныхТаблиц для решения этой задачи.

Почему нельзя соединить объекты ВыборкаИзРезультатаЗапроса?

Давайте проанализируем природу объектов 1С. Объект ВыборкаИзРезультатаЗапроса — это курсор, который позволяет последовательно обходить строки, полученные из базы данных. Он предназначен для чтения, а не для манипуляции структурой данных. Попытка использовать методы вроде ДополнитьТаблицу() или оператор «+» к выборкам приведет к ошибке, так как эти инструменты работают с коллекциями в памяти (например, ТаблицаЗначений), а не с потоками данных из СУБД.

Чтобы соединить данные, нам нужно перенести их в среду, где СУБД сможет выполнить операцию JOIN (соединение). Именно для этого служит МенеджерВременныхТаблиц.

Метод 1: Использование Менеджера временных таблиц для хранения промежуточных результатов

Рассмотрим ситуацию: у нас есть два независимых запроса. В первом мы получаем цены номенклатуры, во втором — фактические суммы из регистров. Нам нужно объединить их по ключевому полю Номенклатура.

Для этого мы создадим один экземпляр МенеджерВременныхТаблиц и будем передавать его разным запросам. Разберем по шагам, как это работает в коде:


// 1. Создаем менеджер временных таблиц
МТ = Новый МенеджерВременныхТаблиц;

// 2. Первый запрос: помещаем данные во временную таблицу ВТ1
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МТ;
Запрос1.Текст = 
    "ВЫБРАТЬ
    |	Цены.Номенклатура КАК Номенклатура,
    |	Цены.Цена КАК Цена
    |ПОМЕСТИТЬ ВТ_Цены
    |ИЗ
    |	РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Цены";
Запрос1.УстановитьПараметр("Период", ТекущаяДата());
Запрос1.Выполнить();

// 3. Второй запрос: помещаем данные во временную таблицу ВТ2
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МТ;
Запрос2.Текст = 
    "ВЫБРАТЬ
    |	Продажи.Номенклатура КАК Номенклатура,
    |	Продажи.СуммаОборот КАК Сумма
    |ПОМЕСТИТЬ ВТ_Продажи
    |ИЗ
    |	РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи";
Запрос2.УстановитьПараметр("Начало", НачалоМесяца(ТекущаяДата()));
Запрос2.УстановитьПараметр("Конец", КонецМесяца(ТекущаяДата()));
Запрос2.Выполнить();

// 4. Третий запрос: соединяем временные таблицы, хранящиеся в МТ
ЗапросИтоговый = Новый Запрос;
ЗапросИтоговый.МенеджерВременныхТаблиц = МТ;
ЗапросИтоговый.Текст = 
    "ВЫБРАТЬ
    |	ВТ_Цены.Номенклатура,
    |	ВТ_Цены.Цена,
    |	ЕСТЬNULL(ВТ_Продажи.Сумма, 0) КАК Сумма
    |ИЗ
    |	ВТ_Цены КАК ВТ_Цены
    |		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Продажи КАК ВТ_Продажи
    |		ПО ВТ_Цены.Номенклатура = ВТ_Продажи.Номенклатура";

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

Важный нюанс: временные таблицы ВТ_Цены и ВТ_Продажи физически существуют в базе данных (в tempdb) до тех пор, пока жив объект МТ или пока вы явно не удалите их запросом с инструкцией УНИЧТОЖИТЬ.

Метод 2: Соединение внешней Таблицы Значений с данными из БД

Часто одна из «выборок» на самом деле является таблицей значений, которую вы рассчитали в коде или получили из внешнего файла. Чтобы соединить её с данными из запроса, её нужно передать в МенеджерВременныхТаблиц в качестве параметра.

Проанализируем пример, где мы передаем таблицу ТЗ_ВнешниеДанные в запрос. Критически важно, чтобы колонки этой таблицы имели четко заданные типы, иначе запрос выдаст ошибку.


// Подготовка таблицы значений с типами
ТЗ = Новый ТаблицаЗначений;
КЧ = Новый КвалификаторыЧисла(15, 2);
ОписаниеТипаЧисло = Новый ОписаниеТипов("Число", КЧ);
ОписаниеТипаНоменклатура = Новый ОписаниеТипов("СправочникСсылка.Номенклатура");

ТЗ.Колонки.Добавить("Номенклатура", ОписаниеТипаНоменклатура);
ТЗ.Колонки.Добавить("Количество", ОписаниеТипаЧисло);

// Заполнение данными...
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("001");
НоваяСтрока.Количество = 10;

// Использование в запросе
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВнешниеДанные.Номенклатура,
    |	ВнешниеДанные.Количество
    |ПОМЕСТИТЬ ВТ_Внешние
    |ИЗ
    |	&ВнешняяТЗ КАК ВнешниеДанные
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ_Внешние.Номенклатура,
    |	ВТ_Внешние.Количество,
    |	ЕСТЬNULL(Остатки.ВНаличииОстаток, 0) КАК СкладскойОстаток
    |ИЗ
    |	ВТ_Внешние КАК ВТ_Внешние
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
    |		ПО ВТ_Внешние.Номенклатура = Остатки.Номенклатура";

Запрос.УстановитьПараметр("ВнешняяТЗ", ТЗ);
Результат = Запрос.Выполнить();

Рекомендации по оптимизации и типичные ошибки

Рассмотрим несколько важных моментов, которые помогут избежать проблем при работе с временными таблицами:

  1. Индексация: Если во временной таблице много строк (больше нескольких сотен), обязательно индексируйте её по полям, по которым будет идти соединение. Используйте инструкцию ИНДЕКСИРОВАТЬ ПО сразу после помещения данных в ВТ.
  2. Типизация: Если вы передаете ТаблицаЗначений в запрос, убедитесь, что у каждой колонки есть ОписаниеТипов. Запрос не может создать структуру таблицы в SQL, если не знает точную длину строк или точность чисел.
  3. Пакетные запросы: Вместо создания нескольких объектов Запрос, как показано в первом методе, лучше использовать один пакетный запрос (тексты разделяются символом «;»). Это сокращает количество обращений к серверу БД и делает код чище.
  4. Очистка памяти: Менеджер временных таблиц потребляет ресурсы сервера. Если вы используете его в длинных циклах, не забывайте вызывать метод МТ.Закрыть() или уничтожать таблицы внутри запроса.

Таким образом, соединение двух «выборок» в 1С — это всегда процесс перевода данных во временные таблицы и выполнение SQL-операции соединения. Использование МенеджерВременныхТаблиц является наиболее гибким и мощным инструментом для решения подобных архитектурных задач.

← На главную