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