Разработка и Конфигурация · 11 мин
Как делать запросы в 1С: практическое руководство
Как писать запросы в 1С: синтаксис языка запросов, работа с конструктором, параметры, соединения, итоги и типичные ошибки начинающих разработчиков.
Запросы в 1С — основной способ получения данных из информационной базы: через них строятся отчёты, заполняются табличные части документов, проверяются остатки и обороты. Язык запросов 1С внешне похож на SQL, но работает не с таблицами базы данных напрямую, а с объектами метаданных: справочниками, документами, регистрами накопления и сведений.
Типичная ошибка новичков — обходить выборку циклом и внутри цикла обращаться к базе за связанными данными. Такой код работает медленно и создаёт лишнюю нагрузку на сервер. Правильный подход — один запрос, который получает все нужные данные сразу, с соединениями, отборами и группировками.
Ниже разобраны основные приёмы: как написать простой запрос, использовать конструктор, передавать параметры, соединять таблицы, получать итоги и обрабатывать результат в коде. Примеры приведены для управляемого приложения, но синтаксис языка запросов одинаков во всех современных конфигурациях на платформе 8.3.
Структура простого запроса
Минимальный запрос состоит из секции ВЫБРАТЬ с перечнем полей и секции ИЗ с указанием таблицы. Таблицей выступает объект метаданных: например, Справочник.Номенклатура или Документ.РеализацияТоваровУслуг. Для виртуальных таблиц регистров указывается полное имя вида РегистрНакопления.ТоварыНаСкладах.Остатки.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Товар,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Товар);
КонецЦикла;
Псевдонимы (КАК) задаются и для полей, и для таблиц — это делает текст читаемым и позволяет обращаться к полям выборки по понятным именам. Результат метода Выполнить() — объект РезультатЗапроса, из которого данные получают либо выборкой (Выбрать), либо выгрузкой в таблицу значений (Выгрузить).
Как собрать текст запроса конструктором
Конструктор запроса избавляет от необходимости помнить имена таблиц и полей. Откройте его из контекстного меню в модуле: правой кнопкой по тексту модуля → «Конструктор запроса». Если курсор стоит внутри существующего текста запроса, конструктор загрузит его для редактирования.
В левой части конструктора выберите базу данных (объекты метаданных), перенесите нужные таблицы в среднюю колонку, затем выберите поля. На закладках «Связи», «Условия», «Группировка», «Итоги» настраиваются соединения, отборы и агрегация. После нажатия «ОК» конструктор сформирует корректный текст запроса автоматически.
- 1Откройте модуль и вызовите контекстное меню → «Конструктор запроса»
- 2Выберите нужные таблицы из списка объектов метаданных
- 3Перенесите поля в список выборки и задайте псевдонимы
- 4На закладке «Условия» добавьте отборы через параметры
- 5Нажмите «ОК» и проверьте сгенерированный текст
- 6Запустите код в отладке и убедитесь, что выборка не пустая
Конструктор полезен и опытным разработчикам: он гарантирует правильность имён полей и синтаксиса соединений. Однако итоговый текст стоит читать и понимать — при оптимизации медленных запросов придётся править его вручную.
Параметры и отборы
Все переменные значения передаются в запрос только через параметры — с помощью символа «&» в тексте и метода УстановитьПараметр(). Подстановка значений конкатенацией строк недопустима: это ломает текст при специальных символах, мешает платформе кэшировать план запроса и приводит к ошибкам с типами дат и ссылок.
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Товар,
| Остатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад) КАК Остатки";
Запрос.УстановитьПараметр("Период", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("Склад", Склад);
Обратите внимание: у виртуальных таблиц регистров параметры (период, условия) указываются в скобках сразу после имени таблицы. Отбор в параметрах виртуальной таблицы работает быстрее, чем тот же отбор в секции ГДЕ, потому что платформа сокращает объём вычисляемых данных ещё на этапе формирования виртуальной таблицы.
Соединения таблиц и объединения
Соединения позволяют получить данные из нескольких таблиц одним запросом. Чаще всего используется ЛЕВОЕ СОЕДИНЕНИЕ: из основной таблицы берутся все строки, а из присоединяемой — только совпавшие по условию. Например, список номенклатуры с остатками, где у части товаров остатка может не быть вовсе.
"ВЫБРАТЬ
| Товары.Ссылка КАК Товар,
| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
|ИЗ
| Справочник.Номенклатура КАК Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК Остатки
| ПО Товары.Ссылка = Остатки.Номенклатура"
Функция ЕСТЬNULL заменяет NULL на заданное значение — без неё в полях из присоединённой таблицы при отсутствии совпадений окажется NULL, что сломает сравнения и арифметику в дальнейшей обработке. ВНУТРЕННЕЕ СОЕДИНЕНИЕ оставляет только строки, где совпадение есть в обеих таблицах; ПРАВОЕ и ПОЛНОЕ используются реже.
Для склейки однотипных наборов данных (например, приход и расход из разных регистров) применяется ОБЪЕДИНИТЬ — с сохранением дублей, или ОБЪЕДИНИТЬ ВСЕ. Помните: ОБЪЕДИНИТЬ без ВСЕ дополнительно устраняет дубликаты, что требует сортировки и замедляет выполнение.
Группировки, итоги и сортировка
Агрегатные функции — СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ — применяются с секцией СГРУППИРОВАТЬ ПО. Все неагрегатные поля из списка выборки обязаны попасть в группировку, иначе запрос не выполнится. Для подсчёта уникальных значений используйте КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле).
Секция ИТОГИ ПО строит иерархические итоги: например, суммы по складу и общий итог. При обходе такой выборки используется ОбходРезультатаЗапроса.ПоГруппировкам, а уровень группировки определяется через ТипЗаписи(). Сортировка задаётся секцией УПОРЯДОЧИТЬ ПО с указанием направления ВОЗР или УБЫВ.
Для ограничения числа строк есть конструкция ВЫБРАТЬ ПЕРВЫЕ N — удобно для отборов «топ-10 товаров» или проверки наличия хотя бы одной записи (ПЕРВЫЕ 1 вместо подсчёта всех строк).
Обработка результата и временные таблицы
Метод Выгрузить() возвращает таблицу значений — её удобно сортировать, сворачивать, искать строки через Найти() и НайтиСтроки(), передавать в табличные части. Метод Выбрать() экономнее по памяти и подходит для последовательного однократного обхода больших объёмов.
Когда логика сложная, запрос разбивают на пакет из нескольких запросов с временными таблицами: промежуточный результат помещается во временную таблицу (ПОМЕСТИТЬ ИмяТаблицы) и используется в последующих запросах того же пакета. Пакет выполняется методом ВыполнитьПакет(), а временные таблицы живут в менеджере временных таблиц.
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Номенклатура КАК Товар
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Док
|ГДЕ
| Док.Ссылка = &Ссылка
|;
|ВЫБРАТЬ
| ВТ.Товар КАК Товар,
| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
|ИЗ
| ВТ_Товары КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, ) КАК Остатки
| ПО ВТ.Товар = Остатки.Номенклатура";
Как проверить результат
Совпадает с ожидаемым числом записей по отбору
Строк заметно больше — вероятно, ошибка в условии соединения
Числа без NULL, итоги сходятся с отчётом в пользовательском режиме
Встречается NULL или суммы завышены из-за дублей
Запрос выполняется за доли секунды на рабочем объёме данных
Заметная пауза даже на небольшой базе — проверьте отборы и индексы
Типичные ошибки и ограничения
Самая частая проблема — запросы в цикле. Если внутри обхода выборки выполняется ещё один запрос, время работы растёт линейно от числа строк. Почти всегда такую логику можно переписать одним запросом с соединением или временной таблицей.
Вторая группа ошибок связана с типами: сравнение ссылки со строкой, передача даты строкой, отбор по составному полю без приведения типа. Платформа в таких случаях либо выдаёт ошибку, либо молча возвращает пустой результат. Проверяйте типы параметров через отладчик.
Учитывайте и ограничения языка: в запросе нельзя изменять данные (только чтение), нет произвольных функций встроенного языка, а набор функций ограничен (дата, строка, ВЫБОР для условной логики). Для сложных вычислений результат выгружают и обрабатывают кодом.
Что важно запомнить
Запрос — это объект с текстом, параметрами и результатом. Пишите текст через конструктор, значения передавайте параметрами, отборы виртуальных таблиц задавайте в их параметрах, соединения сопровождайте ЕСТЬNULL, а сложную логику разбивайте на временные таблицы. Один хороший запрос всегда лучше множества запросов в цикле.
Частые вопросы
Чем отличаются Выбрать() и Выгрузить()?
Выбрать() возвращает выборку для последовательного однократного обхода — экономно по памяти, но нельзя вернуться к предыдущей строке. Выгрузить() создаёт таблицу значений в памяти: её можно сортировать, искать строки и обходить многократно, но на больших объёмах это затратно.
Как проверить, что запрос ничего не вернул?
У результата запроса есть свойство Пустой: Результат.Пустой(). Либо проверяйте вход в цикл выборки — если Следующий() сразу вернул Ложь, записей нет. Для таблицы значений проверяйте Количество() = 0.
Можно ли использовать запрос вне отчёта?
Да, запросы применяются в любом серверном коде: в модулях объектов, общих модулях, обработчиках проведения, регламентных заданиях. В клиентском контексте выполнение запроса недоступно — вызывайте серверную процедуру.
Что такое консоль запросов и зачем она нужна?
Это внешняя обработка, позволяющая выполнять и отлаживать запросы в режиме предприятия на реальных данных без изменения конфигурации. Удобна для проверки текста запроса и параметров перед переносом в код.
Как получить остатки на конкретную дату?
Используйте виртуальную таблицу Остатки регистра накопления с параметром-периодом: РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, ). Период обычно передают как границу или конец дня нужной даты, чтобы учесть движения внутри дня.
Почему запрос в отладчике работает, а в боевой базе медленно?
Чаще всего причина в объёме данных и отсутствии подходящих индексов по полям отбора и соединения. Проверьте, что отборы идут по индексированным полям (измерения регистров, реквизиты с признаком индексирования), а отборы виртуальных таблиц заданы в их параметрах, а не в секции ГДЕ.