Язык запросов 1С 8.3 на практике: от первой выборки до пакета с временными таблицами
Содержание
Запрос в 1С делают объектом Новый Запрос: пишут текст на языке запросов (ВЫБРАТЬ … ИЗ … ГДЕ …), передают параметры через УстановитьПараметр(), выполняют Выполнить() и обходят результат выборкой. Текст можно написать руками или собрать мышкой в конструкторе запроса — он открывается из контекстного меню модуля в конфигураторе.
Что выбрать? Конструктор удобен, когда таблиц много и не помнишь имена полей. Ручной текст быстрее для коротких выборок и правок. Опытные разработчики делают запросы в 1С так: каркас собирают в конструкторе, а тонкие места (условия виртуальных таблиц, индексы временных таблиц) дописывают вручную. Всё ниже относится к платформе 8.3 и работает в любой конфигурации; примеры с документами взяты из 1С:Бухгалтерии 3.0.
Из чего состоит запрос 1С и как выполнить его из кода
Язык запросов 1С — это SQL-подобный язык только для чтения. Изменить или удалить данные запросом нельзя, для этого есть объекты и наборы записей.
Порядок секций в тексте фиксирован:
| Секция | Зачем | Обязательна |
|---|---|---|
ВЫБРАТЬ | Список полей, ПЕРВЫЕ N, РАЗЛИЧНЫЕ, РАЗРЕШЕННЫЕ | Да |
ПОМЕСТИТЬ | Сохранить результат во временную таблицу | Нет |
ИЗ | Источники и соединения | Да (кроме выборки констант-выражений) |
ГДЕ | Отбор строк до группировки | Нет |
СГРУППИРОВАТЬ ПО | Группировка | Нет |
ИМЕЮЩИЕ | Отбор после группировки | Нет |
ОБЪЕДИНИТЬ [ВСЕ] | Склейка с другим запросом | Нет |
УПОРЯДОЧИТЬ ПО | Сортировка | Нет |
ИТОГИ … ПО | Промежуточные итоги | Нет |
Минимальный рабочий пример — контрагенты с ИНН, начинающимся на 77:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Контрагент,
| Контрагенты.ИНН КАК ИНН
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| НЕ Контрагенты.ПометкаУдаления
| И Контрагенты.ИНН ПОДОБНО &Маска
|
|УПОРЯДОЧИТЬ ПО
| ИНН";
Запрос.УстановитьПараметр("Маска", "77%");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Строка(Выборка.Контрагент) + " — " + Выборка.ИНН);
КонецЦикла;
Что здесь происходит:
- 🔹
|в начале строки — продолжение многострочного литерала во встроенном языке, к языку запросов отношения не имеет. - 🔹
КАКзадаёт псевдоним. По нему потом обращаются к полю выборки:Выборка.ИНН. - 🔹
&Маска— параметр. Значение подставляет платформа, склеивать текст запроса со значениями не нужно. - 🔹
%вПОДОБНО— любое количество символов,_— ровно один символ.
Результат можно получить тремя способами:
Результат.Выбрать()— построчный обход, самый экономный по памяти.Результат.Выгрузить()— таблица значений целиком, если её нужно передать дальше или показать на форме.Результат.Пустой()— быстрая проверка «есть ли хоть одна строка» без обхода.
Ключевые слова пишут в любом регистре и на английском (SELECT, FROM, WHERE). В типовых конфигурациях принят русский вариант заглавными буквами — так текст легче читать рядом с кодом.
Конструктор запроса: как собрать текст без ручного набора
В конфигураторе откройте модуль, щёлкните правой кнопкой в нужном месте и выберите Конструктор запроса.... Если курсор стоит внутри уже готового текста запроса, конструктор откроет его для правки. Есть и вариант Конструктор запроса с обработкой результата — он сразу допишет код с Новый Запрос, выполнением и циклом по выборке.
Основные вкладки и что на них делать:
| Вкладка | Что настраивают |
|---|---|
| Таблицы и поля | Слева дерево метаданных, перетаскиваете таблицы и поля в выбранные |
| Связи | Соединения: какая таблица с какой, по какому условию, «Все» слева или справа |
| Группировка | Поля группировки и агрегатные функции для суммируемых полей |
| Условия | Отборы ГДЕ; галочка «Произвольное» позволяет написать условие вручную |
| Дополнительно | ПЕРВЫЕ N, РАЗЛИЧНЫЕ, РАЗРЕШЕННЫЕ, режим временной таблицы |
| Объединения/Псевдонимы | Несколько запросов через ОБЪЕДИНИТЬ и имена полей |
| Порядок | Сортировка |
| Итоги | Поля для ИТОГИ ПО |
Пакет из нескольких запросов собирается там же: каждый запрос пакета — отдельная закладка внизу окна, а на вкладке «Дополнительно» запрос можно сделать временной таблицей и задать её имя.
Для виртуальных таблиц регистров у конструктора есть кнопка «Параметры виртуальной таблицы» над списком выбранных таблиц. Именно туда переносят условия по измерениям — почему это важно, разобрано в разделе про оптимизацию.
Проверить запрос без конфигуратора можно внешней обработкой «Консоль запросов»: в пользовательском режиме откройте её через главное меню Файл → Открыть. Консоль показывает результат, время выполнения и содержимое временных таблиц пакета.
Соединения таблиц в запросах 1С
Соединение берёт строки из двух источников и склеивает их по условию после ПО. Вид соединения решает, что делать со строками без пары.
| Соединение | Что попадает в результат | Где применяют |
|---|---|---|
ВНУТРЕННЕЕ СОЕДИНЕНИЕ | Только строки, у которых нашлась пара | Документы и их строки, где пара есть всегда |
ЛЕВОЕ СОЕДИНЕНИЕ | Все строки левой таблицы, справа NULL, если пары нет | Номенклатура + остатки: показать и товары с нулём |
ПРАВОЕ СОЕДИНЕНИЕ | То же, но главная — правая | Почти не нужно: то же самое пишут левым, поменяв таблицы местами |
ПОЛНОЕ СОЕДИНЕНИЕ | Все строки обеих таблиц | Сравнение двух источников: план и факт |
Пример: все товары из списка и их цены, даже если цены нет:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
ЕСТЬNULL(Цены.Цена, 0) КАК Цена
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цены КАК Цены
ПО Номенклатура.Ссылка = Цены.Номенклатура
ГДЕ
НЕ Номенклатура.ЭтоГруппа
Три вещи, которые нужно знать про соединения:
- ⚠️
NULLсправа. При левом соединении поле без пары равноNULL. Сравнение= 0для него ложно, сумма с ним даётNULL. Оборачивайте такие поля вЕСТЬNULL(Поле, 0), а проверяйте конструкциейПоле ЕСТЬ NULL. - ⚠️ Условие на правую таблицу в
ГДЕломает левое соединение. Если написатьГДЕ Цены.Цена > 0, строки без цены отсекутся, и соединение фактически станет внутренним. Условие на правую таблицу ставят вПО:ПО Номенклатура.Ссылка = Цены.Номенклатура И Цены.Цена > 0. - ⚠️ Точка — это тоже соединение. Запись
Товары.Ссылка.Контрагент.ИННплатформа превращает в неявные левые соединения. Для поля составного типа соединений будет столько, сколько типов в составе. Если нужен один тип, уточните его:ВЫРАЗИТЬ(Хозрасчетный.Субконто1 КАК Справочник.Номенклатура).Артикул.
Параметры, виртуальные и временные таблицы
Как передать параметры в запрос
Любое значение извне — дату, ссылку, число, список — передают через параметр &Имя:
Запрос.УстановитьПараметр("Начало", НачалоМесяца(ДатаОтчета));
Запрос.УстановитьПараметр("Конец", КонецМесяца(ДатаОтчета));
Запрос.УстановитьПараметр("Склады", МассивСкладов);
Для условия Склад В (&Склады) подходит массив или список значений. Таблицу значений можно передать как источник ИЗ &Таблица КАК Т, но только с помещением во временную таблицу, и у колонок должны быть заданы типы.
Виртуальные таблицы регистров
У регистров накопления есть виртуальные таблицы Остатки, Обороты и ОстаткиИОбороты, у регистров сведений — СрезПервых и СрезПоследних. В скобках передаются период и условие, которое платформа применит до расчёта.
Пример на условном регистре накопления ОстаткиТоваров с измерениями Склад и Номенклатура:
ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура,
Остатки.КоличествоОстаток КАК Количество
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(&Дата, Склад = &Склад) КАК Остатки
Остатки на дату &Дата считаются на начало этого момента: движения с точно таким же временем в расчёт не попадут. Чтобы их учесть, передают границу: Новый Граница(Дата, ВидГраницы.Включая).
Временные таблицы и пакетные запросы
Временная таблица — это промежуточный результат, к которому следующий запрос пакета обращается как к обычной таблице. Запросы пакета разделяют точкой с запятой и выполняют методом ВыполнитьПакет(): он возвращает массив результатов по порядку.
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТ_Список
|ИЗ
| &ТаблицаТоваров КАК Т
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Список.Номенклатура КАК Номенклатура,
| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
|ИЗ
| ВТ_Список КАК ВТ_Список
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки(
| &Дата,
| Номенклатура В
| (ВЫБРАТЬ ВТ.Номенклатура ИЗ ВТ_Список КАК ВТ)) КАК Остатки
| ПО ВТ_Список.Номенклатура = Остатки.Номенклатура";
Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаТоваров.Скопировать(, "Номенклатура"));
Запрос.УстановитьПараметр("Дата", Новый Граница(ТекущаяДатаСеанса(), ВидГраницы.Включая));
Результаты = Запрос.ВыполнитьПакет();
Выборка = Результаты[1].Выбрать(); // второй запрос пакета, нумерация с нуля
Здесь остатки считаются только по товарам из переданной таблицы, а не по всему регистру.
- 🔸
ИНДЕКСИРОВАТЬ ПОесть смысл ставить на поля, по которым временную таблицу потом соединяют, если в ней тысячи строк. Для десятка строк индекс ничего не даёт. - 🔸 Менеджер временных таблиц нужен, если таблица должна пережить один пакет и использоваться другим объектом
Запрос. Внутри одного пакета временные таблицы видны и без него. - 🔸 Таблицу, которая больше не нужна, удаляют командой
УНИЧТОЖИТЬ ВТ_Список— это освобождает память на сервере при длинных пакетах.
Группировка, агрегатные функции и итоги
Агрегатные функции считают значение по группе строк: СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ. Все поля, которые выбираются без агрегатной функции, должны быть в СГРУППИРОВАТЬ ПО.
Пример для 1С:Бухгалтерии 3.0: продажи по контрагентам и номенклатуре за период, только где сумма больше 100 000 ₽, с итогом по каждому контрагенту:
ВЫБРАТЬ
Товары.Ссылка.Контрагент КАК Контрагент,
Товары.Номенклатура КАК Номенклатура,
СУММА(Товары.Количество) КАК Количество,
СУММА(Товары.Сумма) КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Ссылка.Проведен
И Товары.Ссылка.Дата МЕЖДУ &Начало И &Конец
СГРУППИРОВАТЬ ПО
Товары.Ссылка.Контрагент,
Товары.Номенклатура
ИМЕЮЩИЕ
СУММА(Товары.Сумма) > 100000
УПОРЯДОЧИТЬ ПО
Сумма УБЫВ
ИТОГИ
СУММА(Количество),
СУММА(Сумма)
ПО
Контрагент
Чем отличаются похожие конструкции:
- 📌
ГДЕрежет строки до группировки,ИМЕЮЩИЕ— готовые группы. Условие на сумму группы можно поставить только вИМЕЮЩИЕ. - 📌
КОЛИЧЕСТВО(*)считает строки,КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Товары.Номенклатура)— уникальные значения. - 📌
СГРУППИРОВАТЬ ПОсхлопывает строки, аИТОГИоставляет детальные строки и добавляет над ними строки-итоги.
Результат с итогами обходят вложенными выборками:
Результат = Запрос.Выполнить();
ВыборкаКонтрагент = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаКонтрагент.Следующий() Цикл
// здесь итог по контрагенту: ВыборкаКонтрагент.Сумма
ВыборкаДетали = ВыборкаКонтрагент.Выбрать();
Пока ВыборкаДетали.Следующий() Цикл
// строки по номенклатуре этого контрагента
КонецЦикла;
КонецЦикла;
В БП поле Сумма в табличной части реализации может включать НДС или нет — это зависит от флага «Сумма включает НДС» в документе. Для отчёта по выручке без налога вычитайте СуммаНДС там, где налог включён в сумму.
Функции языка запросов, без которых не обходится ни один отчёт
| Функция | Что делает | Пример |
|---|---|---|
ЕСТЬNULL(А, Б) | Подставляет Б, если А равно NULL | ЕСТЬNULL(Остатки.КоличествоОстаток, 0) |
ВЫБОР КОГДА … ТОГДА … ИНАЧЕ … КОНЕЦ | Условное значение | ВЫБОР КОГДА Сумма > 0 ТОГДА "Долг" ИНАЧЕ "Аванс" КОНЕЦ |
ВЫРАЗИТЬ(Поле КАК Тип) | Приведение типа | ВЫРАЗИТЬ(Комментарий КАК СТРОКА(100)) |
НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ) | Начало периода | Группировка продаж по месяцам |
ДОБАВИТЬКДАТЕ(Дата, ДЕНЬ, 10) | Сдвиг даты | Плановая дата оплаты |
РАЗНОСТЬДАТ(Д1, Д2, ДЕНЬ) | Разница в единицах | Дней просрочки |
ПОДСТРОКА(Стр, 1, 10) | Часть строки | Первые символы артикула |
ПРЕДСТАВЛЕНИЕ(Ссылка) | Текстовое представление | Выгрузка в файл без лишних обращений |
ССЫЛКА | Проверка типа ссылки | Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг |
В ИЕРАРХИИ | Элемент и все подчинённые | Номенклатура В ИЕРАРХИИ (&Группа) |
Отдельный случай — строки неограниченной длины (например, реквизит «Комментарий»). Их нельзя сравнивать, группировать и использовать в РАЗЛИЧНЫЕ. Приведите поле к фиксированной длине через ВЫРАЗИТЬ(… КАК СТРОКА(N)) или возьмите ПОДСТРОКА.
Почему запрос тормозит или падает: оптимизация и типичные ошибки
Что ускоряет запросы 1С
- Не выполняйте запрос в цикле. Сто запросов по одной строке медленнее одного запроса по ста строкам. Соберите ссылки в массив или таблицу значений и передайте одним параметром.
- Условия по измерениям — в параметры виртуальной таблицы.
Остатки(&Дата, Склад = &Склад)считает остатки только по одному складу. То же условие вГДЕзаставит платформу посчитать остатки по всем складам и потом отбросить лишнее. - Не разыменовывайте составные поля через точку.
Регистратор.Датау регистра даёт соединение с таблицей каждого документа-регистратора. Берите полеПериодиз самого регистра или уточняйте тип черезВЫРАЗИТЬ. - Вложенный запрос в соединении замените временной таблицей. У временной таблицы есть индекс и известный размер, у вложенного запроса — нет.
- Не оборачивайте поле в функцию в условии.
ГОД(Документ.Дата) = 2025не использует индекс по дате, аДокумент.Дата МЕЖДУ &Начало И &Конециспользует. - Выбирайте только нужные поля.
ВЫБРАТЬ *в рабочем коде тянет все реквизиты, включая хранилища значений. - Ограничивайте выборку. Для проверки «есть ли хоть что-то» хватит
ВЫБРАТЬ ПЕРВЫЕ 1.
Ключевое слово РАЗРЕШЕННЫЕ нужно, если для пользователя настроены ограничения доступа на уровне записей. Без него запрос пользователя с ограничениями упадёт с ошибкой доступа, а с ним вернёт только доступные записи.