Работа с объектом СхемаЗапроса (QuerySchema) в 1С:Предприятии — это мощный и надежный способ динамического изменения запросов. В отличие от простой конкатенации строк или использования функции СтрЗаменить, СхемаЗапроса понимает структуру запроса, его пакеты, таблицы и связи. Однако при попытке добавить поле, которое выбирается из временной таблицы, разработчики часто сталкиваются с ошибкой "Поле не найдено". В этой статье мы подробно разберем, почему это происходит и как правильно «протащить» новое поле через всю цепочку временных таблиц в пакете.
Проанализируем ситуацию: когда мы загружаем текст запроса в СхемаЗапроса, объект строит внутреннее дерево метаданных этого запроса. Если в запросе используется временная таблица (например, Т), схема жестко фиксирует список ее полей на основе того фрагмента запроса, где эта таблица была создана (команда ПОМЕСТИТЬ).
Если мы попытаемся добавить поле в оператор ВЫБРАТЬ, обращаясь к этой таблице (например, Т.НовыйРеквизит), система выполнит проверку соответствия. Поскольку в описании источника Т такого поля изначально не было, СхемаЗапроса выдаст исключение. Рассмотрим типичный ошибочный код:
ТекстЗапроса =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура
|ИЗ
| Т КАК Т";
СхемаЗапроса = Новый СхемаЗапроса;
СхемаЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);
// Попытка добавить поле напрямую приведет к ошибке "Поле не найдено"
ОсновнаяВыборка = СхемаЗапроса.ПакетЗапросов[0].Операторы[0];
ОсновнаяВыборка.ВыбираемыеПоля.Добавить("Т.ТипНоменклатуры");
Чтобы избежать этой ошибки, нам нужно действовать последовательно: сначала объявить поле в источнике данных, а уже затем добавлять его в список выбора.
Разберем основной механизм решения. У каждого источника данных в схеме запроса есть коллекция ДоступныеПоля. Это своего рода «декларация» того, что мы можем получить из конкретной таблицы. Если мы работаем с временной таблицей, которая пришла «извне» или была сформирована ранее, нам нужно принудительно расширить этот список.
Рассмотрим пошаговый алгоритм:
ПакетЗапросов.ВЫБРАТЬ в пакете).Источники.ДоступныеПоля этого источника.ВыбираемыеПоля оператора.Вот как выглядит исправленный код:
// 1. Инициализация схемы
СхемаЗапроса = Новый СхемаЗапроса;
СхемаЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);
// 2. Получаем доступ к оператору выбора
ОператорВыбора = СхемаЗапроса.ПакетЗапросов[0].Операторы[0];
// 3. Добавляем поле в доступные поля источника (например, первого источника в запросе)
// Это "легализует" использование поля в дальнейшем
ОператорВыбора.Источники[0].Источник.ДоступныеПоля.Добавить("ТипНоменклатуры");
// 4. Теперь добавляем поле в список выбора без ошибок
ОператорВыбора.ВыбираемыеПоля.Добавить("Т.ТипНоменклатуры");
ТекстРезультата = СхемаЗапроса.ПолучитьТекстЗапроса();
В реальных задачах запрос часто состоит из нескольких пакетов, где данные последовательно перекладываются из одной временной таблицы в другую. Если нам нужно добавить поле Артикул в финальную выборку, а оно появляется только в самом первом пакете, нам придется модифицировать каждый этап.
Проанализируем комплексный подход к решению этой задачи:
ПОМЕСТИТЬ). Добавим поле в список выбора этого пакета. С этого момента временная таблица в контексте запроса уже содержит новое поле.СхемаЗапроса может автоматически не «подхватить» изменение. Поэтому мы используем метод добавления в ДоступныеПоля, описанный выше.ВыбираемыеПоля нужно использовать псевдоним таблицы, заданный в запросе (например, Т.Поле, а не просто Поле), чтобы избежать неоднозначности при соединениях.Если ваш запрос содержит объединения (несколько операторов ВЫБРАТЬ, соединенных ключевым словом ОБЪЕДИНИТЬ), необходимо соблюдать строгое правило: количество и порядок полей во всех частях объединения должны совпадать.
Рассмотрим, как это реализовать программно. Пройдем циклом по всем операторам коллекции:
Для Каждого ТекущийОператор Из СхемаЗапроса.ПакетЗапросов[ИндексПакета].Операторы Цикл
// Добавляем поле в каждый блок ОБЪЕДИНИТЬ
ТекущийОператор.ВыбираемыеПоля.Добавить("Т.НовоеПоле");
КонецЦикла;
При использовании СхемаЗапроса следует учитывать несколько технических нюансов, которые помогут сделать код более надежным:
1. Поиск источника по имени. Не всегда нужная таблица находится под индексом 0. Безопаснее искать источник по псевдониму или имени таблицы. Для этого можно перебирать коллекцию Источники и проверять свойство Источник.ИсточникДанных.
2. Типизация полей. При добавлении поля через ДоступныеПоля.Добавить() вы работаете с объектом ПолеСхемыЗапроса. В некоторых случаях (особенно для сложных временных таблиц) полезно явно указывать типы данных, чтобы планировщик запросов 1С мог построить оптимальный план выполнения.
3. Использование "фейковых" таблиц. Если структура запроса слишком сложна для парсера схемы в текущем контексте, иногда применяют прием с подменой текста запроса на упрощенный (где нужные поля уже описаны в ВЫБРАТЬ ПЕРВЫЕ 1 NULL КАК Поле... ПОМЕСТИТЬ Т), производят манипуляции со схемой, а затем возвращают реальные источники данных.
4. Сложные конструкции. Помните, что СхемаЗапроса может некорректно обрабатывать специфические расширения языка запросов, используемые в СКД (например, обращения к параметрам через & в определенных местах или специфические функции). В таких редких случаях стоит комбинировать работу со схемой и строковые функции обработки текста.
Подводя итог, можно сказать, что ключ к успешному добавлению полей во временные таблицы через СхемаЗапроса лежит в понимании объектной модели: Источник -> Доступные поля -> Выбираемые поля. Соблюдение этой последовательности гарантирует отсутствие ошибок и корректную генерацию итогового текста запроса.