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