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