Разработка и Конфигурация · 12 мин
Таблица значений как параметр запроса в 1С
Как передать таблицу значений в запрос 1С как параметр: установка параметра, временные таблицы, соединение с ТЗ, типичные ошибки и примеры кода.
Передача таблицы значений (ТЗ) в запрос — стандартный приём при разработке на платформе 1С:Предприятие. Он нужен, когда набор данных уже сформирован программно (загружен из файла, рассчитан, получен из другого запроса) и его требуется сопоставить с данными информационной базы: подобрать цены по списку номенклатуры, проверить остатки по списку документов, отфильтровать справочник по произвольному перечню значений.
Механизм работает так: таблица значений передаётся в запрос через метод УстановитьПараметр(), в тексте запроса она помещается во временную таблицу конструкцией ПОМЕСТИТЬ, а дальше используется в соединениях и условиях как обычная таблица. Без помещения во временную таблицу обратиться к параметру-ТЗ напрямую в тексте запроса нельзя — это ключевое ограничение синтаксиса.
Ниже разобран полный цикл: подготовка ТЗ, установка параметра, текст запроса с временной таблицей, варианты соединений, типичные ошибки и альтернативные подходы для случаев, когда передача ТЗ неоптимальна.
Подготовка таблицы значений к передаче в запрос
Перед передачей в запрос таблица значений должна иметь явно описанные колонки с типами. Если колонки добавлены без указания типа, платформа попытается определить тип по данным, но это ненадёжно: пустая колонка или смешанные значения приведут к ошибке при выборке. Правильный подход — создавать колонки через ОписаниеТипов.
Пример подготовки таблицы со списком номенклатуры и количеством:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура",
Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ.Колонки.Добавить("Количество",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Номенклатура = СсылкаНаТовар;
НоваяСтрока.Количество = 10;
Обратите внимание на типы ссылочных колонок. Если в колонке лежит ссылка на справочник, а тип объявлен как строка, соединение с таблицей справочника в запросе не сработает корректно. Тип колонки ТЗ должен совпадать с типом поля, с которым она будет соединяться.
Пустая таблица значений — допустимый параметр. Запрос выполнится, временная таблица просто не будет содержать строк. Это удобно, когда ТЗ формируется динамически и может оказаться пустой: отдельная проверка в коде не обязательна, но иногда полезна, чтобы не выполнять заведомо пустой запрос.
Установка параметра и текст запроса
Параметр устанавливается методом УстановитьПараметр() до выполнения запроса. В тексте запроса параметр-ТЗ используется только в предложении ПОМЕСТИТЬ — напрямую в ГДЕ или соединении сослаться на &ТЗ нельзя.
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ВнешняяТЗ", ТЗ);
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Номенклатура КАК Номенклатура,
| ТЗ.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ВнешняяТЗ КАК ТЗ
|;
|
|ВЫБРАТЬ
| ВТ.Номенклатура КАК Номенклатура,
| ВТ.Количество КАК Количество,
| ЕСТЬNULL(Цены.Цена, 0) КАК Цена
|ИЗ
| ВТ_Данные КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних() КАК Цены
| ПО ВТ.Номенклатура = Цены.Номенклатура";
Результат = Запрос.Выполнить().Выгрузить();
Имена колонок во временной таблице берутся из имён колонок таблицы значений. Синонимы (КАК) в первом выборе задавать не обязательно, но рекомендуется — так текст запроса читается проще и устойчивее к переименованиям.
Менеджер временных таблиц и повторное использование
Если временная таблица нужна в нескольких последовательных запросах, используйте МенеджерВременныхТаблиц. Без него временные таблицы уничтожаются после выполнения пакета, и каждый запрос должен заново помещать ТЗ.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.УстановитьПараметр("ВнешняяТЗ", ТЗ);
Запрос.Текст =
"ВЫБРАТЬ ТЗ.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТ_Номенклатура
|ИЗ &ВнешняяТЗ КАК ТЗ";
Запрос.Выполнить();
// Дальше ВТ_Номенклатура доступна в других запросах
// с тем же менеджером временных таблиц
Менеджер временных таблиц живёт, пока жив объект-переменная. Когда он больше не нужен, можно явно присвоить переменной Неопределено, чтобы освободить память сервера — особенно актуально при больших таблицах в длительных обработках.
- 1Создайте таблицу значений и явно опишите типы колонок через ОписаниеТипов
- 2Заполните ТЗ данными
- 3Создайте объект Запрос и при необходимости МенеджерВременныхТаблиц
- 4Вызовите Запрос.УстановитьПараметр("Имя", ТЗ)
- 5В тексте запроса поместите параметр во временную таблицу: ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ ИЗ &Имя
- 6Напишите основной запрос с соединением к временной таблице
- 7Выполните запрос и проверьте результат выборкой или выгрузкой
Варианты соединения и фильтрации по ТЗ
Левое соединение — сохранить все строки ТЗ
Левое соединение используется, когда нужно получить данные базы по каждой строке ТЗ, даже если соответствия в базе нет. Классический сценарий — подобрать цены или остатки по загруженному из Excel списку: отсутствующие позиции должны попасть в результат с пустыми значениями, чтобы их можно было показать пользователю как не найденные.
Внутреннее соединение — только совпадения
Внутреннее соединение оставляет только строки, для которых нашлось соответствие в базе. Подходит для проверки: какие документы из списка реально существуют, какие контрагенты зарегистрированы. Строки без совпадений отбрасываются.
Фильтрация через В (ВЫБРАТЬ ...)
Если соединение не нужно, а требуется только отфильтровать таблицу базы по значениям из ТЗ, используйте условие с подзапросом:
ВЫБРАТЬ
| Док.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
| Док.Контрагент В (ВЫБРАТЬ ВТ.Контрагент ИЗ ВТ_Контрагенты КАК ВТ)
Такой вариант читается проще соединения и не размножает строки, если в ТЗ встречаются дубли значений.
Сравнение вариантов
Сохраняет все строки ТЗ, не найденные получают NULL. Подходит для загрузок и сверок. Требует ЕСТЬNULL для числовых полей.
Только совпавшие строки. Подходит для проверки существования. Дубли в ТЗ умножат строки результата.
Фильтрация без соединения, дубли в ТЗ не влияют на результат. Не позволяет вытащить дополнительные колонки из ТЗ.
Типичные ошибки и ограничения
Самая частая ошибка — попытка использовать параметр-ТЗ напрямую в условии ГДЕ Поле В (&ТЗ). Для списка значений это работает (массив или список передаётся как параметр без временной таблицы), а для таблицы значений — нет: платформа выдаст ошибку о несоответствии типов. ТЗ требует именно помещения во временную таблицу.
Вторая группа ошибок связана с типами и именами колонок. Если колонка ТЗ называется так же, как поле таблицы базы, а псевдонимы не расставлены, запрос может вернуть неоднозначность. Если тип колонки не совпадает с типом поля соединения, соединение молча не найдёт совпадений или запрос упадёт с ошибкой — зависит от СУБД и режима совместимости.
Производительность — отдельная тема. Таблица значений при передаче в запрос сериализуется и материализуется на стороне СУБД. Для таблиц в десятки тысяч строк это заметная нагрузка. Если ТЗ большая и используется только для фильтрации по одному полю, часто эффективнее передать массив значений и использовать В (&Массив) без временной таблицы.
Альтернативы передаче ТЗ
Если нужна только фильтрация по одному полю, передайте массив или список значений обычным параметром и используйте ГДЕ Поле В (&Список). Это проще по коду и обычно не уступает по скорости на умеренных объёмах.
Если данные уже находятся в базе (например, результат другого запроса), не выгружайте их в ТЗ и не передавайте обратно — соединяйтесь подзапросом или оформляйте первый запрос как временную таблицу в общем менеджере. Каждый лишний обход «база → клиент/серверный код → база» — потерянное время.
Для загрузки внешних данных, которые будут использоваться многократно в течение длительной обработки, иногда рациональнее записать данные в промежуточный регистр сведений и соединяться с ним, чем каждый раз материализовать большую ТЗ.
Как выбрать действие
Проверка результата
После выполнения запроса проверьте количество строк результата: при левом соединении оно должно быть не меньше числа строк ТЗ (больше — при дублях ключей в базе), при внутреннем — не больше. Расхождение с ожиданием почти всегда означает дубли в ТЗ или в соединяемой таблице.
Проверьте поля, заполненные через соединение: NULL в числовых колонках — признак забытых ЕСТЬNULL. Для отладки удобно временно выполнить отдельный запрос ВЫБРАТЬ * ИЗ ВТ_Данные с тем же менеджером временных таблиц и посмотреть, что реально попало во временную таблицу.
Как проверить результат
Не меньше числа строк исходной ТЗ
Меньше — ТЗ не попала в запрос целиком; сильно больше — дубли в соединяемой таблице
Числа или 0 там, где соответствия нет
NULL — не применён ЕСТЬNULL
Все строки и колонки ТЗ на месте, типы корректны
Пусто или типы ссылок определены как строки
Что важно запомнить
ТЗ передаётся через УстановитьПараметр и обязательно помещается во временную таблицу конструкцией ПОМЕСТИТЬ. Типы колонок объявляйте явно, при левом соединении используйте ЕСТЬNULL, для фильтрации по одному полю предпочитайте массив значений, а для повторного использования — МенеджерВременныхТаблиц.
Частые вопросы
Можно ли использовать параметр-ТЗ в условии ГДЕ напрямую?
Нет. Таблицу значений нельзя вставить в условие вида ГДЕ Поле В (&ТЗ) — платформа вернёт ошибку типов. Сначала поместите ТЗ во временную таблицу через ПОМЕСТИТЬ, затем соединяйтесь с ней или используйте подзапрос В (ВЫБРАТЬ ... ИЗ ВТ). Для прямой передачи в ГДЕ подходят массив и список значений.
Почему соединение с временной таблицей не находит совпадений?
Чаще всего не совпадают типы: колонка ТЗ объявлена без типа или как строка, а соединяется со ссылочным полем. Проверьте описание типов колонок при создании ТЗ. Вторая причина — лишние пробелы или разный регистр в строковых ключах при загрузке из внешних файлов.
Нужен ли индекс для временной таблицы из ТЗ?
Для небольших таблиц индекс не обязателен. Если ТЗ большая и по ней идёт соединение, можно добавить предложение ИНДЕКСИРОВАТЬ ПО с указанием полей соединения — это ускоряет соединение на значительных объёмах. На малых таблицах эффект незаметен.
Как передать ТЗ в запрос динамического списка?
Напрямую в произвольный запрос динамического списка ТЗ передать нельзя — динамический список не поддерживает временные таблицы из параметров. Обходные пути: фильтрация через отбор по списку значений, либо замена динамического списка на таблицу значений формы, заполняемую обычным запросом.
Что быстрее: ТЗ с временной таблицей или массив в параметре?
Если нужна фильтрация по одному полю, массив с условием В (&Массив) обычно проще и не медленнее. ТЗ оправдана, когда из неё нужно несколько колонок одновременно — например, номенклатура вместе с количеством и ценой для последующего соединения. Точный ответ для конкретных объёмов даёт только замер на реальных данных.
Можно ли передать в запрос таблицу значений с колонкой типа ТаблицаЗначений?
Нет. Вложенные таблицы значений в параметр запроса не передаются — при помещении во временную таблицу такая колонка вызовет ошибку. Структуру «шапка + строки» передают двумя связанными ТЗ или одной плоской таблицей с колонкой-ключом.