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

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

Работа с запросом как с объектом в 1С

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

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

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

Материал ориентирован на разработчиков, которые уже знакомы с синтаксисом языка запросов 1С, но хотят системно разобраться в программном интерфейсе объекта: какие свойства за что отвечают, когда выгружать результат в таблицу значений, а когда обходить выборку, и как не потерять производительность на ровном месте.

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

Объект создаётся конструктором Новый Запрос. Конструктор может принимать текст запроса сразу, но чаще текст назначается через свойство Текст — так код легче читать и сопровождать. Минимальный рабочий шаблон выглядит так: создать объект, присвоить текст, установить параметры, выполнить, обработать результат.

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

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

"ВЫБРАТЬ

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

| Номенклатура.Наименование КАК Наименование

|ИЗ

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

|ГДЕ

| Номенклатура.Родитель = &Группа";

Запрос.УстановитьПараметр("Группа", НужнаяГруппа);

РезультатЗапроса = Запрос.Выполнить();

Выборка = РезультатЗапроса.Выбрать();

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

// обработка строки

КонецЦикла;

Обратите внимание на разделение ответственности: свойство Текст хранит только статический текст на языке запросов, а все переменные значения попадают в запрос исключительно как параметры. Это не стилистическая рекомендация, а защита от ошибок типизации и от неявных проблем с кешированием планов выполнения на СУБД.

Параметры запроса: установка и типичные ловушки

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

Особое внимание — параметрам-коллекциям. В условие В (&Список) можно передать массив, список значений или таблицу значений. Частая ошибка — передать один элемент вместо коллекции: запрос отработает, но разработчик потом удивляется, почему условие ведёт себя иначе, чем ожидалось. Обратная ловушка — передача пустого массива: условие В (&ПустойМассив) не вернёт ни одной строки, что логично, но не всегда очевидно в момент отладки.

Даты как параметры требуют аккуратности с границами периодов. Если нужно выбрать документы за день включительно, передавайте КонецДня(Дата) или используйте виртуальные таблицы с параметром границы — объект Граница с видом включения или исключения точки. Сравнение «меньше или равно» с датой без времени обрежет весь день, кроме его начала.

Типичные ошибки с параметрами
01
Конкатенация значений прямо в текст запроса вместо параметров — риск ошибок типизации и проблем с производительностью СУБД
02
Передача одиночного значения в условие В (&Параметр) вместо массива — неочевидное поведение отбора
03
Пустой массив в параметре — запрос возвращает пустой результат, что принимают за ошибку логики
04
Дата без времени в условии <= — теряются все записи за день, кроме полуночи

Результат запроса: выборка или выгрузка

Метод Выполнить() возвращает объект РезультатЗапроса. У него два основных способа получить данные: Выбрать() — выборка для последовательного построчного обхода, и Выгрузить() — таблица значений (или дерево значений при обходе по группировкам) целиком в памяти.

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

У выборки есть важный нюанс: обход типа ПоГруппировкам и ПоГруппировкамСИерархией позволяет строить вложенные циклы по итогам запроса без выгрузки в дерево. Это эффективно для отчётных обходов, но требует внимательности: внутри обхода по группировкам доступны только поля текущего уровня, а детальные записи получают вложенной выборкой через Выборка.Выбрать().

Выбрать() или Выгрузить()

Выбрать()

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

Выгрузить()

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

Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией)

Дерево значений по итогам запроса. Подходит для иерархических отображений, требует итогов в тексте запроса

Временные таблицы и менеджер временных таблиц

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

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

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

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

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

"ВЫБРАТЬ Товары.Номенклатура, Товары.Количество

|ПОМЕСТИТЬ ВТ_Отбор

|ИЗ Документ.Реализация.Товары КАК Товары

|ГДЕ Товары.Ссылка = &Документ";

Запрос1.УстановитьПараметр("Документ", ДокументСсылка);

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

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

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

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

"ВЫБРАТЬ ВТ.Номенклатура, Цены.Цена

|ИЗ ВТ_Отбор КАК ВТ

|ЛЕВОЕ СОЕДИНЕНИЕ ...";

Результат = Запрос2.Выполнить();

Временные таблицы живут, пока жив менеджер. После использования менеджер стоит закрыть методом Закрыть() — это освобождает ресурсы СУБД, что заметно на нагруженных системах. Если менеджер не назначен явно, платформа создаёт его неявно, и таблица умирает вместе с выполнением запроса — повторно обратиться к ней из другого объекта уже нельзя.

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

Пакетные запросы

Один объект запроса может содержать несколько операторов ВЫБРАТЬ, разделённых точкой с запятой. Метод ВыполнитьПакет() выполняет их все и возвращает массив результатов в порядке следования запросов в тексте. Это удобно, когда нужны сразу несколько наборов данных: например, шапка документа, его табличная часть и итоговые остатки.

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

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

Порядок работы с пакетным запросом
  1. 1
    Сформируйте текст: операторы ВЫБРАТЬ разделяются точкой с запятой, временные таблицы создаются ПОМЕСТИТЬ
  2. 2
    Установите все параметры через УстановитьПараметр для каждого &amp;Параметра в тексте
  3. 3
    Вызовите ВыполнитьПакет() и получите массив результатов
  4. 4
    Обратитесь к нужному результату по индексу: Результаты[0], Результаты[1] и т.д.
  5. 5
    При отладке замените вызов на ВыполнитьПакетСПромежуточнымиДанными() и проверьте содержимое временных таблиц

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

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

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

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

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

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

РезультатЗапроса.Пустой()
Норма

Возвращает Ложь при ожидаемых данных

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

Истина при данных, которые должны быть — проверяйте параметры и отборы

Количество строк после Выгрузить()
Норма

Совпадает с результатом того же текста в консоли запросов

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

Меньше, чем в консоли — вероятно, отличается значение или тип параметра

Типы колонок выгруженной таблицы
Норма

Типы соответствуют ожиданиям дальнейшей обработки

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

Составной тип там, где ожидался конкретный — уточните выражения в тексте запроса

Производительность и ограничения

Объект запроса сам по себе лёгкий, но текст и структура данных определяют нагрузку на СУБД. Главные правила: выбирать только нужные поля, а не *; использовать виртуальные таблицы регистров (Остатки, Обороты, СрезПоследних) вместо соединений с физическими таблицами движений; передавать период в параметры виртуальной таблицы, а не фильтровать в ГДЕ после неё.

Запросы в цикле — классический антипаттерн. Если внутри цикла обхода выполняется новый запрос, почти всегда правильнее переписать логику на один запрос с соединением или временной таблицей. Тысяча итераций с запросом внутри превращается в тысячу обращений к СУБД, тогда как один запрос с группировкой решает задачу за одно обращение.

Ограничения, о которых стоит помнить: запрос только читает данные (запись выполняют объекты и наборы записей); текст запроса не может быть изменён «на лету» частями — только целиком через свойство Текст или построитель запроса; в толстом клиенте и на сервере один и тот же текст обычно работает одинаково, но обращения к данным всегда выполняются на сервере в клиент-серверном режиме.

Частые ошибки и как их избежать

Ошибка «Поле не найдено» при обращении к выборке означает, что поля нет в результате или у него другой псевдоним. Проверяйте секцию ВЫБРАТЬ и псевдонимы через КАК — обращение в коде идёт именно по псевдониму.

Ошибка несоответствия типов возникает, когда параметр содержит значение не того типа, что ожидает поле сравнения: например, строка вместо ссылки. Лечится явным приведением в коде до установки параметра или функцией ВЫРАЗИТЬ в тексте запроса.

Ещё одна группа проблем — дубли строк при соединениях. Если запрос вдруг возвращает больше строк, чем ожидалось, ищите соединение «один ко многим» без группировки или без отбора по периоду в виртуальной таблице среза. СрезПоследних без указания периода вернёт записи по всем датам, что почти никогда не нужно.

Маршрут решения

Как выбрать подход

Нужно один раз прочитать данные по простому условиюОдин объект Запрос с параметрами и обходом через Выбрать()
Промежуточный отбор используется в нескольких запросахОбщий МенеджерВременныхТаблиц и ПОМЕСТИТЬ в первом запросе
Нужно несколько независимых наборов данных за одно обращениеПакетный запрос с ВыполнитьПакет()
Данные нужны в форме или требуют повторного обходаВыгрузить() в таблицу значений
Логика требует запроса внутри циклаПерепишите на один запрос с соединением или временной таблицей

FAQ

Чем отличается Выполнить() от ВыполнитьПакет()?

Выполнить() выполняет первый (или единственный) запрос в тексте и возвращает один РезультатЗапроса. ВыполнитьПакет() выполняет все запросы, разделённые точкой с запятой, и возвращает массив результатов в порядке их следования в тексте.

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

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

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

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

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

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

Нужно ли явно закрывать менеджер временных таблиц?

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

Итог

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

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