Запросы в 1С 8.3: синтаксис, конструктор и примеры кода

· 9 мин

Содержание

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

Минимальный рабочий запрос — две секции: ВЫБРАТЬ (какие поля нужны) и ИЗ (откуда брать). Всё остальное — отбор, соединения, группировку, сортировку — добавляют по мере надобности. Ниже разберём синтаксис по секциям, покажем конструктор и консоль, соединения и временные таблицы, параметры, итоги и приёмы ускорения медленных запросов.

Язык запросов читает данные, но не меняет их. Записать, провести, удалить объект запросом нельзя — для этого работают с самими объектами в коде.

Из каких секций состоит текст запроса

Порядок секций в языке запросов жёсткий. Если переставить ГДЕ и ИЗ местами — платформа выдаст ошибку синтаксиса. Запоминается по цепочке «что → откуда → при каком условии → как сгруппировать → как отсортировать».

Основные секции:

  • 🔹 ВЫБРАТЬ — перечень полей результата. ВЫБРАТЬ * тянет все поля, но так делать не стоит: лишние данные замедляют запрос.
  • 🔹 ИЗ — таблица-источник (справочник, документ, регистр) и её псевдоним.
  • 🔹 ГДЕ — условие отбора строк.
  • 🔹 СГРУППИРОВАТЬ ПО — свёртка строк с агрегатными функциями.
  • 🔹 ИМЕЮЩИЕ — отбор уже по сгруппированным данным.
  • 🔹 УПОРЯДОЧИТЬ ПО — сортировка результата.
  • 🔹 ИТОГИ ... ПО — многоуровневые итоги по иерархии.

Вот как выглядит простой запрос к справочнику номенклатуры:

ВЫБРАТЬ
    Номенклатура.Наименование КАК Наименование,
    Номенклатура.Артикул КАК Артикул
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    НЕ Номенклатура.ПометкаУдаления
УПОРЯДОЧИТЬ ПО
    Наименование

Обратите внимание на три детали. Полное имя таблицы — Справочник.Номенклатура, с указанием типа объекта. Псевдоним через КАК задают и таблице, и каждому полю — по псевдониму потом удобно обращаться к колонкам результата. Служебные слова языка (ВЫБРАТЬ, ИЗ, ГДЕ) принято писать заглавными: платформе регистр безразличен, но читаемость растёт.

Два полезных уточнения в секции ВЫБРАТЬ:

  • ВЫБРАТЬ РАЗЛИЧНЫЕ — убирает дубли строк.
  • ВЫБРАТЬ ПЕРВЫЕ 100 — ограничивает выборку первой сотней строк (пригодится для проверки на лету).

В секции ВЫБРАТЬ поле можно не только взять как есть, но и вычислить. Для условного значения служит конструкция ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ — аналог «если-иначе». Например, так помечают просроченные заказы:

ВЫБОР
    КОГДА Заказ.СрокОплаты < &Сегодня
        ТОГДА "Просрочен"
    ИНАЧЕ "В срок"
КОНЕЦ КАК Статус

Строковые поля складывают оператором +, числа и даты обрабатывают встроенными функциями языка — ПОДСТРОКА, ДАТАВРЕМЯ, РАЗНОСТЬДАТ. Логика переносится в запрос, и постобработка в коде почти не нужна.

Конструктор запроса против ручного набора

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

Когда удобнее конструктор:

  • 💡 первый запрос к незнакомой таблице — видно все доступные поля и реквизиты;
  • 💡 сложные соединения нескольких таблиц — связи рисуются мышью;
  • 💡 виртуальные таблицы регистров — конструктор сам подставит параметры.

Когда быстрее руками:

  • 💡 мелкая правка готового текста — не нужно каждый раз открывать окно;
  • 💡 типовой короткий запрос, который вы пишете по памяти.

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

Отдельно стоит Конструктор запроса с обработкой результата — он вдобавок к тексту сгенерирует код обхода выборки. Для быстрого прототипа это экономит время.

Как запрос выполняется из кода

Сам по себе текст ничего не возвращает. Чтобы получить данные, создают объект Запрос, задают текст, при необходимости — параметры, и вызывают метод Выполнить():

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    Номенклатура.Ссылка КАК Ссылка,
    |    Номенклатура.Наименование КАК Наименование
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    Номенклатура.Родитель = &Группа";

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

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

Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Наименование);
КонецЦикла;

Вертикальная черта | в начале строк — это продолжение строкового литерала, а не часть языка запросов. Так многострочный текст запроса удобно разбивать на строки прямо в коде.

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

Соединения таблиц и временные таблицы

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

Типы соединений:

СоединениеЧто берётКогда применять
СОЕДИНЕНИЕ (внутреннее)только совпавшие строки обеих таблицнужны записи, которые есть и там, и там
ЛЕВОЕ СОЕДИНЕНИЕвсе строки левой + совпавшие правойсохранить все строки основной таблицы
ПОЛНОЕ СОЕДИНЕНИЕвсе строки обеих таблицсверка двух списков целиком

Условие связи задают в секции ПО:

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    Остатки.КоличествоОстаток КАК Остаток
ИЗ
    Справочник.Номенклатура КАК Товары
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
    ПО Товары.Ссылка = Остатки.Номенклатура

Левое соединение с подзапросом — главная причина тормозов. Если справа стоит не таблица, а вложенный ВЫБРАТЬ, оптимизатор СУБД не может построить нормальный план. Такой подзапрос почти всегда выносят во временную таблицу.

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

ВЫБРАТЬ
    Продажи.Номенклатура КАК Номенклатура,
    СУММА(Продажи.Количество) КАК Количество
ПОМЕСТИТЬ ВтПродажи
ИЗ
    РегистрНакопления.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура
;
ВЫБРАТЬ
    ВтПродажи.Номенклатура,
    ВтПродажи.Количество
ИЗ
    ВтПродажи КАК ВтПродажи

Чтобы пакет с временными таблицами работал, объекту запроса назначают менеджер временных таблиц:

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

Большую временную таблицу, по которой потом идёт соединение, стоит индексировать: допишите ИНДЕКСИРОВАТЬ ПО и поле связи. Без индекса соединение по тысячам строк снова упрётся в полный перебор.

Параметры: как сделать запрос гибким

Жёстко зашитые значения в тексте — плохая идея. Период, организацию, вид операции выносят в параметры. Параметр в тексте помечают амперсандом: &ДатаНачала, &Организация. Значение подставляют методом УстановитьПараметр:

Запрос.УстановитьПараметр("ДатаНачала", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("Организация", ВыбраннаяОрганизация);

Параметры дают сразу несколько выгод:

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

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

РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатка, Склад = &Склад)

Первый параметр здесь — момент времени, второй — условие отбора. Отбор внутри виртуальной таблицы работает в разы быстрее, чем тот же отбор в секции ГДЕ снаружи: платформа отфильтрует данные до расчёта остатков, а не после.

Если нужно отобрать по списку значений — например, по нескольким складам сразу — в параметр передают не одно значение, а коллекцию (массив или список значений), а в тексте пишут оператор В:

ГДЕ Склад В (&СписокСкладов)

Это удобнее и быстрее, чем длинная цепочка условий через ИЛИ.

Итоги, группировка и агрегатные функции

Когда из множества строк нужно получить сводку — сумму, количество, максимум — применяют агрегатные функции вместе с СГРУППИРОВАТЬ ПО.

Доступные функции:

  • СУММА(...) — сумма значений;
  • КОЛИЧЕСТВО(...) — число строк; КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) — число уникальных значений;
  • МАКСИМУМ(...) и МИНИМУМ(...) — крайние значения;
  • СРЕДНЕЕ(...) — среднее.

Правило простое: все поля в ВЫБРАТЬ, которые не завёрнуты в агрегатную функцию, обязаны стоять в СГРУППИРОВАТЬ ПО. Иначе платформа не поймёт, как свернуть строки.

ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    СУММА(Продажи.Сумма) КАК Выручка,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Продажи.Регистратор) КАК ЧислоПродаж
ИЗ
    РегистрНакопления.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Контрагент
ИМЕЮЩИЕ
    СУММА(Продажи.Сумма) > 100000

Секция ИМЕЮЩИЕ отбирает уже по агрегату: в примере останутся только контрагенты с выручкой свыше 100 000. Отличие от ГДЕ принципиальное: ГДЕ отсеивает строки до группировки, ИМЕЮЩИЕ — после.

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

Как ускорить медленный запрос

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

  1. Соединение с вложенным подзапросом. Вынесите подзапрос во временную таблицу и проиндексируйте поле связи.
  2. Условие по неиндексированному полю. Отбирайте по измерениям регистров и реквизитам, которые индексированы; по произвольному реквизиту фильтр идёт полным перебором.
  3. Получение данных «через точку» от составного поля. Запись вида Регистратор.Дата для поля составного типа порождает скрытое соединение со всеми возможными таблицами. Ограничьте тип оператором ВЫРАЗИТЬ(... КАК Документ.РеализацияТоваровУслуг).
  4. Условие ИЛИ по разным полям. Такой отбор часто мешает использовать индекс. Иногда выгоднее разбить запрос через ОБЪЕДИНИТЬ ВСЕ.
  5. Ручной расчёт остатков. Вместо свёртки движений вручную берите готовую виртуальную таблицу .Остатки или .ОстаткиИОбороты.

ОБЪЕДИНИТЬ без ВСЕ дополнительно убирает дубли — это лишняя сортировка и нагрузка. Если дублей заведомо нет, пишите ОБЪЕДИНИТЬ ВСЕ.

Диагностика: выберите симптом

Отладка в консоли запросов

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

Как обычно работают с консолью:

  1. Открывают консоль запросов через Файл → Открыть (это внешняя обработка, .epf) либо из меню функций технического специалиста, если консоль встроена в конфигурацию.
  2. Вставляют или собирают текст запроса — в консоли тоже есть конструктор.
  3. Заполняют значения параметров.
  4. Выполняют и смотрят результат в таблице; видны и время выполнения, и число строк.

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

Проверьте себя
Какая секция отбирает строки ДО группировки?
Что быстрее при отборе по складу в остатках?
Чем завершают каждый запрос в пакете с временными таблицами?

Частые вопросы

Можно ли запросом изменить или удалить данные?
Нет. Язык запросов только читает. Чтобы записать, провести или пометить объект на удаление, работают с самим объектом в коде — запрос лишь находит нужные ссылки.
Чем отличается `ОБЪЕДИНИТЬ` от `ОБЪЕДИНИТЬ ВСЕ`?
ОБЪЕДИНИТЬ склеивает выборки и убирает дубли строк, тратя время на сортировку. ОБЪЕДИНИТЬ ВСЕ просто складывает строки без проверки на дубли и работает быстрее. Если дублей нет, всегда берите вариант со ВСЕ.
Зачем нужен менеджер временных таблиц?
Он хранит временные таблицы между запросами пакета. Без назначенного менеджера таблица, созданная через ПОМЕСТИТЬ, будет недоступна следующему запросу, и платформа сообщит, что таблица не найдена.
Почему запрос возвращает лишние строки?
Чаще всего виновато соединение «один ко многим»: одной строке левой таблицы соответствует несколько строк правой. Либо сверните данные группировкой, либо уберите повторы через ВЫБРАТЬ РАЗЛИЧНЫЕ.
Нужно ли знать язык запросов, если есть конструктор?
Конструктор соберёт текст за вас, но читать и править готовый запрос, искать причину тормозов и понимать чужой код без знания секций не получится. Базовый синтаксис стоит освоить в любом случае.