К статье
Практический материал

Разработка и Конфигурация · 14 мин

Как получить данные из таблицы значений в запросе 1С

Практическое руководство: как передать таблицу значений в запрос 1С через параметр, использовать ПОМЕСТИТЬ, соединить с другими таблицами и избежать типичных ошибок.

Таблица значений — один из самых востребованных объектов встроенного языка 1С: в неё удобно собирать промежуточные данные, результаты обработок, загруженные из файлов списки номенклатуры или контрагентов. Но таблица значений живёт только в памяти, и напрямую обратиться к ней в тексте запроса нельзя. Чтобы использовать её данные в запросе, таблицу нужно передать через параметр и описать её структуру в самом тексте запроса.

Типовой сценарий выглядит так: вы заполняете таблицу значений в коде, передаёте её в запрос через Запрос.УстановитьПараметр(), помещаете во временную таблицу оператором ПОМЕСТИТЬ, а затем соединяете с данными информационной базы — справочниками, регистрами, документами. Это позволяет одним запросом получить, например, остатки по списку товаров, загруженному из Excel, или актуальные цены для произвольного набора позиций.

Ниже разобран полный рабочий цикл: подготовка таблицы значений, передача её в запрос, описание полей и типов, соединение с таблицами базы, типичные ошибки и способы проверить результат.

Подготовка таблицы значений перед передачей в запрос

Перед тем как таблица попадёт в запрос, у неё должны быть явно описаны колонки с типами. Если вы создаёте таблицу в коде сами — добавьте колонки через ТаблицаЗначений.Колонки.Добавить() с указанием типа. Это важно: запрос 1С строго типизирован, и колонка без типа или со смешанным содержимым может привести к ошибке или неожиданному результату при соединениях.

Для колонок, по которым планируется соединение с данными базы (номенклатура, контрагенты, документы), задавайте ссылочный тип — например, Новый ОписаниеТипов("СправочникСсылка.Номенклатура"). Для строк и чисел указывайте длину и точность, соответствующие данным базы: строка артикула в 30 символов не соединится корректно с полем другой длины в некоторых сценариях сравнения.

Если таблица получена из внешнего источника (загрузка из Excel, обмен, результат другого запроса, выгруженный через Выгрузить()), проверьте, что в ней нет пустых строк с незаполненными ключевыми колонками. Лишние строки с NULL или пустыми ссылками увеличат результат соединения и усложнят отладку.

Порядок действий
  1. 1
    Создайте таблицу значений и явно опишите колонки с типами через Колонки.Добавить()
  2. 2
    Заполните таблицу данными, контролируя отсутствие пустых ключевых значений
  3. 3
    Создайте объект Запрос и задайте текст с конструкцией ИЗ &ПараметрТЗ
  4. 4
    Передайте таблицу: Запрос.УстановитьПараметр("ТЗ", ТаблицаЗначений)
  5. 5
    Поместите таблицу во временную через ПОМЕСТИТЬ и соедините с данными базы
  6. 6
    Выполните запрос и проверьте результат через Выгрузить() или Выбрать()

Передача таблицы в запрос через параметр

Механизм прост: таблица значений передаётся как обычный параметр запроса, а в тексте запроса к ней обращаются как к источнику данных через символ &. Ключевое отличие от обычных параметров — обязательное явное перечисление полей в секции ВЫБРАТЬ. Конструкция ВЫБРАТЬ * для таблицы-параметра не поддерживается: платформа не знает состав колонок на этапе компиляции запроса.

Минимальный рабочий пример:

ТЗ = Новый ТаблицаЗначений;

ТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));

ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));

// ... заполнение ТЗ данными ...

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| ТЗ.Номенклатура КАК Номенклатура,

| ТЗ.Количество КАК Количество

|ПОМЕСТИТЬ ВТ_Данные

|ИЗ

| &ТЗ КАК ТЗ";

Запрос.УстановитьПараметр("ТЗ", ТЗ);

Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

Запрос.Выполнить();

Обратите внимание на три момента. Во-первых, имя параметра в УстановитьПараметр() должно совпадать с именем в тексте запроса без символа &. Во-вторых, псевдоним таблицы (КАК ТЗ) обязателен — через него адресуются поля. В-третьих, если вы используете ПОМЕСТИТЬ и планируете обращаться к временной таблице в следующих пакетах запроса, нужен менеджер временных таблиц.

Использование ПОМЕСТИТЬ и временных таблиц

Прямое обращение к таблице-параметру возможно только один раз в одном пакете запроса. Если данные таблицы нужны в нескольких местах сложного запроса, стандартный приём — сразу поместить её во временную таблицу через ПОМЕСТИТЬ, а дальше работать уже с ней. Это также улучшает читаемость и позволяет оптимизатору СУБД построить план выполнения.

Временная таблица живёт в рамках менеджера временных таблиц. Если менеджер не назначен, платформа создаст его неявно, но тогда временные таблицы будут доступны только в пределах одного вызова Выполнить(). Для цепочки из нескольких запросов создайте менеджер явно и передайте его каждому объекту запроса.

Пример соединения временной таблицы с регистром остатков:

Запрос.Текст =

"ВЫБРАТЬ

| ТЗ.Номенклатура КАК Номенклатура,

| ТЗ.Количество КАК Количество

|ПОМЕСТИТЬ ВТ_Данные

|ИЗ

| &ТЗ КАК ТЗ

|;

|

|ВЫБРАТЬ

| ВТ.Номенклатура,

| ВТ.Количество,

| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток

|ИЗ

| ВТ_Данные КАК ВТ

| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки

| ПО ВТ.Номенклатура = Остатки.Номенклатура";

Левое соединение здесь принципиально: оно гарантирует, что все строки вашей таблицы попадут в результат, даже если по позиции нет остатков. Функция ЕСТЬNULL() заменяет NULL на ноль, чтобы дальше с полем можно было работать как с числом без дополнительных проверок.

bsl
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТЗ", ТаблицаЗначений);
Запрос.Текст = "ВЫБРАТЬ ТЗ.Ссылка КАК Ссылка ПОМЕСТИТЬ ВТ ИЗ &ТЗ КАК ТЗ";
Запрос.Выполнить();

Типизация полей и соединение с данными базы

Самая частая причина «пустого» результата при соединении таблицы значений с таблицами базы — несовпадение типов. Если в колонке таблицы значений лежит строка с кодом номенклатуры, а вы соединяете её с полем ссылочного типа, соединение не сработает: строка никогда не равна ссылке. Перед соединением приведите данные к нужному типу либо в коде при заполнении таблицы, либо в самом запросе.

Если в таблице у вас только коды или артикулы, а нужны ссылки, соединяйтесь со справочником по реквизиту и уже из него забирайте ссылку: ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Спр ПО ВТ.Артикул = Спр.Артикул. Учтите, что артикул может быть не уникальным — тогда соединение размножит строки. Проверьте уникальность ключа заранее или добавьте группировку.

Для числовых полей следите за разрядностью: если колонка описана как Число(10,0), а вы загружаете дробные количества, дробная часть будет отсечена ещё на этапе заполнения таблицы, и итоги в запросе не сойдутся с исходными данными.

Сравнение вариантов

Соединение по ссылке

Самый быстрый и надёжный вариант. Требует, чтобы в таблице значений уже были ссылки нужного типа — например, полученные ранее запросом или через НайтиПоКоду().

Соединение по реквизиту (код, артикул)

Подходит при загрузке из внешних файлов. Медленнее и рискованнее: возможны дубли и нестрогое совпадение строк из-за пробелов и регистра.

ВЫРАЗИТЬ для приведения типа

Позволяет привести поле составного типа к конкретному прямо в запросе. Работает только с допустимыми для ВЫРАЗИТЬ типами, строку в ссылку так не превратить.

Типичные ошибки и как их диагностировать

Первая группа ошибок — синтаксические: платформа сообщает «Поле не найдено» или «Ошибка в схеме запроса». Причина почти всегда в том, что имя поля в запросе не совпадает с именем колонки таблицы значений, либо использована конструкция ВЫБРАТЬ *. Проверьте имена колонок через отладчик, остановившись на строке перед Выполнить().

Вторая группа — логические: запрос выполняется, но результат пустой или содержит меньше строк, чем ожидалось. Типичные причины: внутреннее соединение вместо левого, несовпадение типов полей соединения, лишние пробелы в строковых ключах, отбор по виртуальной таблице, который отсекает часть данных. Диагностируйте поэтапно: сначала выполните запрос, который просто выбирает содержимое ИЗ &ТЗ без соединений, и убедитесь, что таблица передалась целиком.

Третья группа — ошибки контекста. Таблица значений большого объёма, переданная в запрос на клиенте в клиент-серверном режиме, будет сериализована и передана на сервер целиком — при сотнях тысяч строк это заметная нагрузка на память и сеть. Для больших объёмов выполняйте обработку в серверной процедуре и рассмотрите пакетную обработку порциями.

Типичные ошибки
01
Использование ВЫБРАТЬ * для таблицы-параметра — запрос падает с ошибкой, поля нужно перечислять явно
02
Колонки без типа или с составным типом — соединения работают непредсказуемо, итоги расходятся
03
ВНУТРЕННЕЕ СОЕДИНЕНИЕ вместо ЛЕВОГО — строки таблицы без совпадений в базе теряются из результата
04
Соединение строки со ссылкой — результат всегда пустой, ошибки при этом платформа не выдаёт
05
Повторное обращение к &ТЗ в другом пакете запроса без ПОМЕСТИТЬ — параметр доступен, но логика запроса усложняется и часто приводит к дублям
06
Забытый МенеджерВременныхТаблиц — временная таблица недоступна в следующем запросе цепочки

Проверка результата и отладка

Базовая проверка — выгрузить результат и посмотреть количество строк: Результат = Запрос.Выполнить().Выгрузить();, затем в отладчике оценить Результат.Количество() и сравнить с количеством строк исходной таблицы. Если при левом соединении строк стало больше, чем в исходной таблице, — ключ соединения не уникален, и в базе нашлось несколько совпадений на одну строку.

Для проверки содержимого временной таблицы в середине цепочки запросов используйте отдельный запрос с тем же менеджером временных таблиц: ВЫБРАТЬ * ИЗ ВТ_Данные — для временной таблицы звёздочка допустима, потому что её структура уже известна платформе. Это самый быстрый способ убедиться, что данные дошли до сервера в ожидаемом виде.

Полезный приём при отладке в консоли запросов (в конфигураторе или внешней обработкой): консоль запросов с поддержкой параметров-таблиц позволяет заполнить таблицу вручную и пошагово проверить текст запроса до переноса его в код.

Контрольные точки

Как проверить результат

Количество строк результата при левом соединении
Норма

Не меньше, чем строк в исходной таблице значений

Тревожный признак

Меньше исходного — значит, часть строк отсечена соединением или отбором

Содержимое полей соединения
Норма

Ссылки заполнены, NULL только там, где совпадений в базе нет

Тревожный признак

Все поля соединения NULL — несовпадение типов или значений ключей

Суммы по числовым полям
Норма

Итог по таблице до запроса совпадает с итогом в результате

Тревожный признак

Сумма выросла — дубли из-за неуникального ключа соединения

Ограничения и альтернативные подходы

Таблица-параметр не поддерживает индексы на стороне СУБД в полном смысле: платформа передаёт её как временный набор данных, и для больших таблиц соединение может выполняться полным перебором. Если таблица содержит десятки тысяч строк и запрос выполняется часто, имеет смысл пересмотреть архитектуру: например, записывать данные в промежуточный регистр сведений или использовать внешние источники данных.

Альтернатива для небольших списков значений — передать массив или список значений и использовать его в условии В (&Список) без соединения. Это проще и быстрее, когда от таблицы нужна только фильтрация по одному полю. Но как только нужно передать несколько связанных колонок (номенклатура + количество + цена), таблица значений остаётся единственным корректным вариантом.

Ещё одно ограничение: поля таблицы-параметра нельзя использовать в операциях, требующих индексированного доступа, а составные типы колонок могут приводить к неявным преобразованиям. По возможности держите структуру таблицы минимальной — передавайте только те колонки, которые реально используются в запросе.

Маршрут решения

Как выбрать действие

Нужно отфильтровать данные по одному полю (список ссылок или кодов)Передайте массив или список значений и используйте условие В (&Список) — таблица значений не нужна
Нужно передать несколько связанных колонок и соединить их с базойИспользуйте таблицу значений через параметр с ПОМЕСТИТЬ во временную таблицу
Таблица содержит сотни тысяч строк и запрос выполняется частоРассмотрите запись данных в промежуточный регистр сведений или пакетную обработку порциями
Данные загружаются из файла и ключи текстовыеСоединяйтесь со справочником по реквизиту и предварительно очистите строки от пробелов через СокрЛП()

Частые вопросы

Можно ли использовать ВЫБРАТЬ * для таблицы-параметра?

Нет. Для таблицы значений, переданной через параметр, платформа требует явного перечисления полей в секции ВЫБРАТЬ. Звёздочка допустима только для временных таблиц и таблиц базы данных, чья структура известна на этапе компиляции запроса.

Почему запрос с таблицей значений возвращает пустой результат?

Чаще всего причина в соединении полей разных типов — например, строки со ссылкой — или во внутреннем соединении, которое отсекает несовпадающие строки. Выполните запрос без соединений, просто выбрав содержимое ИЗ &ТЗ, чтобы убедиться, что данные передались, а затем добавляйте соединения по одному.

Нужен ли МенеджерВременныхТаблиц, если запрос один?

Если весь текст выполняется одним вызовом Выполнить() и временная таблица используется только внутри этого пакета, явный менеджер не обязателен — платформа создаст его неявно. Явный менеджер нужен, когда к временным таблицам обращаются несколько последовательных запросов.

Как передать в запрос таблицу, полученную из результата другого запроса?

Вызовите у результата метод Выгрузить() — он возвращает таблицу значений с типизированными колонками, которую можно сразу передать в следующий запрос через УстановитьПараметр(). Однако если оба запроса можно объединить в один пакет с временной таблицей, это предпочтительнее: данные не покидают СУБД и не сериализуются.

Есть ли ограничение на размер передаваемой таблицы?

Жёсткого ограничения платформа не накладывает, но практический предел определяется памятью сервера и стоимостью передачи данных. Таблицы в сотни тысяч строк с несколькими колонками могут заметно нагружать сервер и клиент-серверное взаимодействие. Для таких объёмов используйте пакетную обработку или промежуточное хранение в регистре.

Можно ли изменить данные базы через таблицу-параметр в запросе?

Нет, язык запросов 1С предназначен только для чтения данных. Таблицу значений можно использовать в запросе для выборки и соединения, но запись в базу выполняется отдельно — объектами (документами, наборами записей регистров) в коде по результатам выборки.

Итог

Что важно запомнить

Таблица значений попадает в запрос только через параметр: УстановитьПараметр() плюс конструкция ИЗ &ТЗ с явным перечислением полей. Помещайте её во временную таблицу через ПОМЕСТИТЬ, соединяйте левым соединением, контролируйте типы колонок и проверяйте результат по количеству строк и итогам. Для фильтрации по одному полю вместо таблицы достаточно массива в условии В (&Список).