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