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