Таблицы запроса 1С — всё, что можно написать после ИЗ: справочники, документы и их табличные части, виртуальные таблицы регистров, временные таблицы и внешняя таблица значений, переданная параметром. Последний вариант нужен постоянно: строки загружены из Excel, собраны в коде или взяты из табличной части документа, и их надо сопоставить с данными базы одним запросом, а не циклом с обращениями к реквизитам через точку.
Ниже — схема «таблица значений → временная таблица → соединение с базой → результат обратно», два примера и разбор ошибки «Тип не может быть выбран в запросе», на которой такой код спотыкается чаще всего.
| Источник | Как пишется после ИЗ | Что важно |
|---|---|---|
| Объектная таблица | Справочник.Номенклатура | Данные базы, у ссылочных полей доступны реквизиты через точку |
| Табличная часть | Документ.РеализацияТоваровУслуг.Товары | Есть поля Ссылка (владелец) и НомерСтроки |
| Виртуальная таблица регистра | РегистрСведений.КурсыВалют.СрезПоследних(&Дата) | Отбор лучше передавать в параметрах таблицы, а не в ГДЕ |
| Временная таблица | ВТТовары (создана через ПОМЕСТИТЬ) | Живёт в пределах пакета или менеджера временных таблиц, её можно индексировать |
| Вложенный запрос | (ВЫБРАТЬ …) КАК Т | В соединениях с большими таблицами лучше заменить временной |
| Таблица значений из параметра | &ДанныеФайла КАК ДанныеФайла | Используется как источник для ПОМЕСТИТЬ, колонки обязательно с типами |
Дальше — о двух последних строках таблицы. Общий синтаксис пакетов, соединений и группировок разобран в статье про язык запросов 1С с примерами.
ОписаниеТипов.ВЫБРАТЬ … ПОМЕСТИТЬ ВТДанные ИЗ &Параметр КАК Т.Выгрузить() или Выбрать().Всё это выполняется на сервере. На тонком и веб-клиенте нет ни объекта Запрос, ни ТаблицаЗначений, поэтому в модуле формы код выносят в процедуру с директивой &НаСервере, а в общем модуле ставят флаг «Сервер».
Почему через временную таблицу: содержимое таблицы значений уходит в СУБД, и надёжнее всего положить его туда запросом ВЫБРАТЬ … ПОМЕСТИТЬ, где таблица-параметр — единственный источник. Соединения и группировки делайте в следующих запросах пакета, заодно временную таблицу можно проиндексировать.
Тип колонки задаётся вторым параметром Колонки.Добавить. Для ссылок — Новый ОписаниеТипов("СправочникСсылка.Номенклатура"), для чисел — с КвалификаторыЧисла, для строк — обязательно с КвалификаторыСтроки и конкретной длиной. Строка без квалификатора становится строкой неограниченной длины, а такие поля нельзя сравнивать в условии соединения, группировать и упорядочивать.
Если таблица без типов колонок пришла из чужого кода и переписывать его не хочется, соберите типизированную копию:
// Возвращает копию таблицы значений, в которой у каждой колонки задан тип.
// Колонкам без типа он собирается по фактическим значениям;
// строки в таких колонках при копировании обрезаются до 150 символов.
Функция ТаблицаСТипизированнымиКолонками(Исходная) Экспорт
Результат = Новый ТаблицаЗначений;
Для Каждого Колонка Из Исходная.Колонки Цикл
ТипКолонки = Колонка.ТипЗначения;
Если ТипКолонки.Типы().Количество() = 0 Тогда
ТипКолонки = ТипПоЗначениямКолонки(Исходная, Колонка.Имя);
КонецЕсли;
Результат.Колонки.Добавить(Колонка.Имя, ТипКолонки);
КонецЦикла;
Для Каждого СтрокаИсходной Из Исходная Цикл
ЗаполнитьЗначенияСвойств(Результат.Добавить(), СтрокаИсходной);
КонецЦикла;
Возврат Результат;
КонецФункции
Функция ТипПоЗначениямКолонки(Таблица, ИмяКолонки)
Типы = Новый Массив;
Для Каждого Значение Из Таблица.ВыгрузитьКолонку(ИмяКолонки) Цикл
ТекущийТип = ТипЗнч(Значение);
Если ТекущийТип = Тип("Неопределено") Или ТекущийТип = Тип("Null") Тогда
Продолжить;
КонецЕсли;
Если Типы.Найти(ТекущийТип) = Неопределено Тогда
Типы.Добавить(ТекущийТип);
КонецЕсли;
КонецЦикла;
Если Типы.Количество() = 0 Тогда
Типы.Добавить(Тип("Строка")); // колонка пустая: тип любой, лишь бы был задан
КонецЕсли;
// Длину строки ограничиваем, чтобы по колонке можно было соединять и группировать.
Возврат Новый ОписаниеТипов(Типы, , Новый КвалификаторыСтроки(150));
КонецФункции
Где тип колонки задан, функция берёт его как есть, где нет — собирает составной тип по значениям, пропуская Неопределено и Null. Колонку, объявленную как ОписаниеТипов("Строка") без длины, она не исправит: такую создайте заново с квалификатором. Значения вроде ТаблицаЗначений или Структура запрос не примет ни в каком виде — эти колонки перед передачей удалите.
Задача из загрузки Excel или CSV: есть колонки Артикул и Количество, для каждой строки нужна ссылка на номенклатуру. НайтиПоРеквизиту в цикле обращается к базе на каждой строке, а пакетный запрос делает всё за один вызов и заодно показывает не найденные и повторяющиеся артикулы.
// Общий модуль с флагом «Сервер» или модуль объекта обработки. Платформа 8.3.
// Справочник.Номенклатура с реквизитом Артикул есть в УТ 11 и БП 3.0;
// в другой конфигурации подставьте свои имена метаданных.
// Сопоставляет строки файла с номенклатурой по артикулу одним запросом.
// Параметры:
// СтрокиФайла - ТаблицаЗначений - колонки Артикул (строка или число)
// и Количество (число); типы колонок могут быть не заданы.
// Возвращаемое значение:
// ТаблицаЗначений - НомерСтроки, Артикул, Количество, Номенклатура, НайденоПозиций.
Функция СопоставитьПоАртикулам(СтрокиФайла) Экспорт
// 1. Таблица для запроса: у каждой колонки явный тип.
ДанныеФайла = Новый ТаблицаЗначений;
ДанныеФайла.Колонки.Добавить("НомерСтроки",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0)));
ДанныеФайла.Колонки.Добавить("Артикул",
Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
ДанныеФайла.Колонки.Добавить("Количество",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
НомерСтроки = 0;
Для Каждого СтрокаФайла Из СтрокиФайла Цикл
НомерСтроки = НомерСтроки + 1;
Артикул = СтрокаФайла.Артикул;
Если ТипЗнч(Артикул) = Тип("Число") Тогда
Артикул = Формат(Артикул, "ЧГ=0"); // без пробела между разрядами
КонецЕсли;
НоваяСтрока = ДанныеФайла.Добавить();
НоваяСтрока.НомерСтроки = НомерСтроки;
НоваяСтрока.Артикул = СокрЛП(Артикул);
НоваяСтрока.Количество = СтрокаФайла.Количество;
КонецЦикла;
// 2. Пакет: таблица значений -> временная таблица -> соединение со справочником.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеФайла.НомерСтроки КАК НомерСтроки,
| ДанныеФайла.Артикул КАК Артикул,
| ДанныеФайла.Количество КАК Количество
|ПОМЕСТИТЬ ВТДанныеФайла
|ИЗ
| &ДанныеФайла КАК ДанныеФайла
|
|ИНДЕКСИРОВАТЬ ПО
| Артикул
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТДанныеФайла.НомерСтроки КАК НомерСтроки,
| ВТДанныеФайла.Артикул КАК Артикул,
| ВТДанныеФайла.Количество КАК Количество,
| ЕСТЬNULL(МАКСИМУМ(СпрНоменклатура.Ссылка),
| ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Номенклатура,
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ СпрНоменклатура.Ссылка) КАК НайденоПозиций
|ИЗ
| ВТДанныеФайла КАК ВТДанныеФайла
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНоменклатура
| ПО ВТДанныеФайла.Артикул = СпрНоменклатура.Артикул
| И ВТДанныеФайла.Артикул <> """"
| И НЕ СпрНоменклатура.ПометкаУдаления
|
|СГРУППИРОВАТЬ ПО
| ВТДанныеФайла.НомерСтроки,
| ВТДанныеФайла.Артикул,
| ВТДанныеФайла.Количество
|
|УПОРЯДОЧИТЬ ПО
| НомерСтроки";
Запрос.УстановитьПараметр("ДанныеФайла", ДанныеФайла);
// 3. Результат последнего запроса пакета -> таблица значений.
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Формат(Артикул, "ЧГ=0"). Из Excel артикул иногда приходит числом. При неявном преобразовании в строку 12345 превратится в «12 345» с разделителем разрядов и ни с чем не совпадёт.КвалификаторыСтроки(50). Колонка Артикул участвует в условии соединения, поэтому длина у неё ограничена.ПОМЕСТИТЬ ВТДанныеФайла … ИНДЕКСИРОВАТЬ ПО Артикул. Первый запрос пакета только переносит данные во временную таблицу. Индекс по полю соединения полезен, когда строк в файле много.Артикул <> "" в ПО. Без него строка файла с пустым артикулом соединится со всеми позициями справочника, у которых артикул тоже не заполнен. В тексте модуля пустая строка записана четырьмя кавычками, потому что кавычки внутри строкового литерала удваиваются.МАКСИМУМ и КОЛИЧЕСТВО(РАЗЛИЧНЫЕ …). Артикул в справочнике не обязан быть уникальным: без группировки дубль размножит строку файла, и количество задвоится. Группировка по номеру строки оставляет одну запись на строку файла, а НайденоПозиций показывает число кандидатов: 0 — не найдено, больше 1 — нужна ручная проверка.ЕСТЬNULL. Для строки без пары левое соединение даёт NULL. Подставляем пустую ссылку, чтобы колонка Номенклатура содержала только ссылки.Выполнить().Выгрузить(). У пакета Выполнить() возвращает результат последнего запроса.Второй частый случай — проверить строки документа. Цикл с условием Строка.Номенклатура.ПометкаУдаления обращается к базе за каждой ссылкой, а запрос обходится одним обращением: выгруженная табличная часть типизирована, и во временной таблице поля ссылки доступны через точку. Подробно про такие обращения — в статье о ссылках и полях через точку в запросе.
// Модуль объекта документа с табличной частью Товары (реквизит Номенклатура).
// Обработчик выполняется на сервере. СтрШаблон: платформа 8.3.6+
// и режим совместимости конфигурации не ниже 8.3.6.
// В более старых конфигурациях на БСП используйте
// СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку.
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.НомерСтроки КАК НомерСтроки,
| Т.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТТовары
|ИЗ
| &Товары КАК Т
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТТовары.НомерСтроки КАК НомерСтроки,
| ВТТовары.Номенклатура КАК Номенклатура
|ИЗ
| ВТТовары КАК ВТТовары
|ГДЕ
| ВТТовары.Номенклатура.ПометкаУдаления
|
|УПОРЯДОЧИТЬ ПО
| НомерСтроки";
// Только нужные колонки; типы колонок берутся из реквизитов табличной части.
Запрос.УстановитьПараметр("Товары", Товары.Выгрузить(, "НомерСтроки, Номенклатура"));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Строка %1: номенклатура «%2» помечена на удаление.",
Формат(Выборка.НомерСтроки, "ЧГ=0"), Выборка.Номенклатура);
Сообщение.Поле = "Товары[" + Формат(Выборка.НомерСтроки - 1, "ЧН=0; ЧГ=0") + "].Номенклатура";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецЦикла;
КонецПроцедуры
Товары.Выгрузить(, "НомерСтроки, Номенклатура") берёт только нужные колонки — цены и суммы в СУБД гонять незачем. В Формат для индекса стоит ЧН=0: без него ноль превращается в пустую строку, и сообщение для первой строки не привяжется к полю. ЧГ=0 убирает разделитель разрядов у номеров больше 999. В конфигурациях на БСП 3.x вместо СообщениеПользователю обычно вызывают ОбщегоНазначения.СообщитьПользователю(Текст, ЭтотОбъект, Поле, , Отказ). Порядок параметров: текст, ключ данных, поле, путь к данным (здесь пропущен), Отказ — его процедура установит в Истина сама. В БСП 2.x серверного варианта нет, там вызывают ОбщегоНазначенияКлиентСервер.СообщитьПользователю с тем же порядком параметров.
| Метод | Что возвращает | Когда использовать |
|---|---|---|
Результат.Выгрузить() | ТаблицаЗначений | Нужен весь набор в памяти: передать дальше, загрузить в табличную часть, отдать в следующий запрос |
Результат.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам) | ДеревоЗначений | Запрос с ИТОГИ, нужна иерархия |
Результат.Выбрать() | ВыборкаИзРезультатаЗапроса | Достаточно одного прохода по строкам, таблица в памяти не нужна |
Запрос.ВыполнитьПакет() | Массив результатов | Нужны результаты нескольких запросов пакета |
Колонки выгруженной таблицы получают типы полей запроса. Если поле пришло из левого соединения без ЕСТЬNULL, в тип колонки добавится Null. Такую таблицу можно снова передать в запрос без подготовки.
Чтобы результат сразу попал в табличную часть, используйте Загрузить: Объект.Товары.Загрузить(ТаблицаРезультата). Колонки сопоставляются по именам, поэтому псевдонимы полей в запросе называйте так же, как реквизиты табличной части. Перед обходом результат удобно проверить на пустоту — варианты разобраны в статье про проверку пустого результата запроса.
Запрос к таблице значений, в котором нет ни одной таблицы базы, — лишний круг: данные уходят в СУБД и возвращаются обратно. Для отбора хватит НайтиСтроки или Скопировать с параметрами отбора, для свёртки — Свернуть("Номенклатура", "Количество"). Запрос нужен, когда требуется хотя бы одно из трёх:
ИМЕЮЩИЕ, вычисляемые поля, ВЫБОР — то, чего нет у Свернуть.Если таблица значений сама получена запросом из этой же базы, не выгружайте её: оставьте временной таблицей в общем менеджере и читайте следующим запросом.
ОписаниеТипов при создании колонки или пересобрать таблицу функцией выше.Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц и передайте этот же менеджер второму запросу.&НаСервере.Объект.Товары в модуле управляемой формы — коллекция данных формы, а не таблица значений. В серверной процедуре передайте в запрос Объект.Товары.Выгрузить().ОписаниеТипов("Строка"). Задайте длину в квалификаторе или приведите поле в запросе: ВЫРАЗИТЬ(Т.Артикул КАК СТРОКА(50)).ЕСТЬNULL. Вызов Строка.Номенклатура.Пустая() на такой строке упадёт с ошибкой «Значение не является значением объектного типа (Пустая)». Оберните поле в ЕСТЬNULL прямо в запросе.Массив используют как значение параметра в условиях: ГДЕ Т.Номенклатура В (&Список). Источником после ИЗ он быть не может. Если нужен именно источник, создайте таблицу значений с одной типизированной колонкой и заполните её из массива в цикле.
Выполните пакет с менеджером временных таблиц, затем тем же объектом запроса прочитайте временную таблицу и выгрузите — содержимое будет видно в отладчике:
// Отладочная функция, выполняется на сервере.
// ДанныеФайла - ТаблицаЗначений с типизированными колонками Артикул и Количество.
Функция СодержимоеВременнойТаблицы(ДанныеФайла) Экспорт
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеФайла.Артикул КАК Артикул,
| ДанныеФайла.Количество КАК Количество
|ПОМЕСТИТЬ ВТДанныеФайла
|ИЗ
| &ДанныеФайла КАК ДанныеФайла";
Запрос.УстановитьПараметр("ДанныеФайла", ДанныеФайла);
Запрос.Выполнить();
// Менеджер тот же, поэтому временная таблица ещё существует.
Запрос.Текст = "ВЫБРАТЬ * ИЗ ВТДанныеФайла";
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Удобнее проверять пакеты в консоли запросов, она показывает и временные таблицы. Подробнее — в статье про консоль запросов 1С 8.3.
Каждую — отдельным параметром с уникальным именем и отдельным запросом пакета: ПОМЕСТИТЬ ВТПервая ИЗ &Первая, затем ПОМЕСТИТЬ ВТВторая ИЗ &Вторая. Соединяйте уже временные таблицы в следующем запросе. Колонки обеих таблиц должны быть типизированы.
Нет. РезультатЗапроса.Выгрузить() и ТабличнаяЧасть.Выгрузить() возвращают таблицу с типами колонок. Проблемы бывают только с таблицами, собранными вручную через Колонки.Добавить без второго параметра.
Если загрузку из файла с сопоставлением номенклатуры или проверку документов нужно встроить в типовую конфигурацию без правки её модулей, это работа для внешней обработки или расширения. Такие задачи входят в услугу «Отчёты и обработки 1С». Опишите, что нужно, и разместите задачу: оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.