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