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

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

Запрос как объект в 1С: практическое руководство

Как работать с объектом Запрос в 1С: установка текста и параметров, выполнение, обход выборки, пакетные запросы, временные таблицы и типичные ошибки разработчика.

Объект «Запрос» во встроенном языке 1С — это программный инструмент, который позволяет выполнять запросы к базе данных из кода, управлять параметрами, временными таблицами и обходить результат разными способами. В отличие от запроса с конструктором в отчёте на СКД, объект «Запрос» даёт полный контроль над процессом: вы сами решаете, когда выполнить запрос, как обойти выборку и что делать с каждой строкой результата.

Типичные сценарии применения — проверка остатков перед проведением документа, подбор данных для обработки, заполнение табличных частей, выгрузка данных в другие системы. Везде, где нужна гибкая логика обработки результата, объект «Запрос» предпочтительнее готовых механизмов отчётов.

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

Создание объекта и установка текста запроса

Объект создаётся стандартным конструктором встроенного языка:

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

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

"ВЫБРАТЬ

| Товары.Ссылка КАК Номенклатура,

| Товары.Наименование КАК Наименование

|ИЗ

| Справочник.Номенклатура КАК Товары

|ГДЕ

| Товары.ПометкаУдаления = ЛОЖЬ";

Текст запроса пишется на языке запросов 1С — он похож на SQL, но оперирует именами объектов метаданных (справочниками, документами, регистрами), а не физическими таблицами СУБД. Платформа сама транслирует запрос в SQL для конкретной базы данных.

Для написания текста удобно использовать конструктор запроса: выделите заготовку текста в модуле, вызовите контекстное меню и выберите «Конструктор запроса». Это снижает риск синтаксических ошибок и опечаток в именах полей. После редактирования текст автоматически обновится в коде.

Текст можно менять динамически — например, подставлять разные условия в зависимости от настроек обработки. Делать это следует аккуратно: конкатенация строк с пользовательскими данными недопустима, все переменные значения передаются только через параметры.

Передача параметров

Параметры задаются через свойство Параметры и метод УстановитьПараметр:

Запрос.УстановитьПараметр("Склад", СкладОтбора);

Запрос.УстановитьПараметр("ДатаОстатков", КонецДня(ДатаОтчёта));

В тексте запроса параметр обозначается символом амперсанда: ГДЕ Остатки.Склад = &Склад. Имя параметра в методе УстановитьПараметр указывается без амперсанда.

В параметры можно передавать примитивные типы, ссылки, массивы и списки значений. Массивы особенно полезны для условия В (&Список) — это стандартный способ отбора по произвольному набору значений. Пустой массив в таком условии вернёт пустой результат, что иногда используют как осознанный приём, но чаще становится источником ошибки «запрос ничего не нашёл».

Для виртуальных таблиц регистров параметры периода и отборов задаются в круглых скобках после имени таблицы: РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад). Отбор лучше задавать именно в параметрах виртуальной таблицы, а не в секции ГДЕ — так платформа сначала ограничит выборку, и запрос выполнится быстрее.

Выполнение запроса и способы обхода результата

Метод Выполнить() возвращает объект «Результат запроса». Дальше есть два основных пути: обход выборки или выгрузка в таблицу значений.

Обход через Выбрать()

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

Пока Выборка.Следующий() Цикл

Сообщить(Выборка.Наименование);

КонецЦикла;

Выборка читается последовательно, строка за строкой. Это экономит память на больших результатах и подходит для потоковой обработки. Метод Выбрать() принимает параметр ОбходРезультатаЗапроса: ПоГруппировкам или ПоГруппировкамСИерархией — если в запросе есть итоги (ИТОГИ ПО), можно обходить группировочные записи и детальные строки вложенными циклами.

Выгрузка в таблицу значений

Таблица = Запрос.Выполнить().Выгрузить();

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

Проверить, вернул ли запрос строки, можно методом Пустой() результата запроса до начала обхода — это избавляет от лишнего кода с флагами «первая строка найдена».

Выбор способа обхода

Выбрать() — последовательный обход

Подходит для больших результатов и разовой обработки. Ограничение: нельзя вернуться к уже прочитанным строкам.

Выгрузить() — таблица значений

Подходит для многократного поиска, сортировки и передачи данных. Ограничение: весь результат загружается в память.

Выгрузить(ПоГруппировкам) — дерево значений

Подходит для иерархических данных с итогами. Требует секции ИТОГИ в тексте запроса.

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

Один объект «Запрос» может содержать несколько запросов, разделённых точкой с запятой. Промежуточные результаты помещаются во временные таблицы через конструкцию ПОМЕСТИТЬ:

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

"ВЫБРАТЬ

| Продажи.Номенклатура КАК Номенклатура,

| СУММА(Продажи.Количество) КАК Количество

|ПОМЕСТИТЬ ВТ_Продажи

|ИЗ

| РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи

|СГРУППИРОВАТЬ ПО

| Продажи.Номенклатура

|;

|

|ВЫБРАТЬ

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

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

|ИЗ

| ВТ_Продажи КАК ВТ

|ГДЕ

| ВТ.Количество > 100";

Метод Выполнить() вернёт результат последнего запроса пакета. Чтобы получить результаты всех запросов, используйте ВыполнитьПакет() — он возвращает массив результатов в порядке следования запросов. Метод ВыполнитьПакетСПромежуточнымиДанными() дополнительно покажет содержимое временных таблиц, что удобно при отладке.

Временные таблицы живут внутри менеджера временных таблиц. Если его не указать, платформа создаст неявный менеджер, и таблицы исчезнут после выполнения. Явное создание менеджера позволяет использовать одни и те же временные таблицы в нескольких объектах «Запрос»:

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

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

Также в менеджер можно загрузить готовую таблицу значений через запрос с источником &Таблица — это стандартный способ передать в запрос данные, которых нет в базе (например, табличную часть документа).

Не увлекайтесь количеством временных таблиц: каждая ПОМЕСТИТЬ — это физическая материализация данных на сервере СУБД. Часто одно соединение или вложенный запрос работают быстрее, чем цепочка из пяти временных таблиц.
ПЗ
Практическое замечание

Запросы в транзакциях и блокировки

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

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

Ещё один нюанс — конструкция ДЛЯ ИЗМЕНЕНИЯ в тексте запроса. Она указывает платформе заблокировать читаемые данные для последующего изменения. Использовать её нужно осознанно: избыточные блокировки снижают параллельность работы, а в файловой базе могут привести к ошибкам превышения таймаута.

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

Проверить корректность запроса до запуска кода помогает консоль запросов — внешняя обработка, которая выполняет текст запроса с заданными параметрами и показывает результат. Переносите в неё текст из модуля вместе со значениями параметров, чтобы воспроизвести проблему изолированно.

В отладчике содержимое выборки и таблицы значений видно через «Вычислить выражение» и табло. Для пакетных запросов полезно временно заменить Выполнить() на ВыполнитьПакетСПромежуточнымиДанными() и посмотреть, на каком этапе данные теряются.

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

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

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

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

Совпадает с ожидаемым числом записей

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

Строк кратно больше — дубли из-за соединения по неуникальному полю

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

Сходятся с данными регистра или отчёта

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

Суммы завышены — задвоение при соединении, занижены — лишний отбор

Время выполнения
Норма

Запрос отрабатывает за приемлемое для интерактивной работы время

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

Заметная пауза — проверьте отборы в параметрах виртуальных таблиц и индексы

Типичные ошибки и ограничения

Разработчики, переходящие на объект «Запрос» после SQL, часто пытаются использовать синтаксис чистого SQL — функции, специфичные для СУБД, или обращения к физическим таблицам. Язык запросов 1С этого не поддерживает: работа идёт только через метаданные, а диалект ограничен возможностями платформы.

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

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

Типичные ошибки
01
Запрос внутри цикла по выборке — многократные обращения к СУБД вместо одного соединения
02
Отбор в секции ГДЕ вместо параметров виртуальной таблицы — чтение лишних данных и медленная работа
03
Передача пользовательских строк конкатенацией в текст запроса — риск некорректной работы и уязвимостей
04
Игнорирование метода Пустой() — обращение к полям выборки, когда строк нет
05
Потеря временных таблиц из-за отсутствия явного менеджера временных таблиц

Часто задаваемые вопросы

Чем объект «Запрос» отличается от запроса в СКД?

В СКД запрос — это источник данных для отчёта, а обход и оформление результата выполняет система компоновки. Объект «Запрос» даёт ручной контроль: вы сами выполняете запрос, обходите строки и реализуете любую логику. Для отчётов обычно выбирают СКД, для алгоритмов и обработок — объект «Запрос».

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

Установите параметр со значением таблицы и используйте её как источник: ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ ИЗ &Таблица КАК Таблица. Поля таблицы значений становятся полями временной таблицы, дальше с ними можно соединять данные базы. Потребуется явный менеджер временных таблиц, если результат нужен в другом запросе.

Почему запрос с условием В (&Список) возвращает пустой результат?

Чаще всего причина в пустом массиве, переданном как параметр: условие В (пустой список) ложно для любой строки. Проверяйте заполненность массива перед выполнением и либо пропускайте запрос, либо подменяйте условие в тексте.

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

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

Можно ли выполнить запрос без прав на читаемые объекты?

Да, если код выполняется в привилегированном модуле или после вызова УстановитьПривилегированныйРежим(Истина). В обычном режиме запрос работает с учётом прав пользователя и вернёт только доступные ему данные.

Что быстрее: обход выборки или выгрузка в таблицу значений?

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

Итог

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

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