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