Как работать с запросами в 1С 8.3: от синтаксиса до ускорения

· 9 мин

Содержание

Запрос в 1С — это не SQL напрямую к базе, а свой язык, который платформа транслирует в SQL сама. Из-за этого частая ошибка новичка — тянуть в запрос UPDATE или DELETE: язык запросов 1С умеет только читать данные, менять их нельзя. Писать и работать с запросами проще, чем кажется: текст набирается в Конфигураторе или через Конструктор, выполняется в коде, результат разбирается выборкой.

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

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

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

Минимальный рабочий код выглядит так:

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |   Номенклатура.Наименование,
    |   Номенклатура.Артикул
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |   НЕ Номенклатура.ПометкаУдаления";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Наименование);
КонецЦикла;

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

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

Если результат нужен сразу таблицей, а не перебором, используйте Результат.Выгрузить() — получите таблицу значений с теми же колонками.

Базовый синтаксис: ВЫБРАТЬ, ИЗ, ГДЕ и группировки

Скелет любого запроса — пять секций, строго в этом порядке:

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

Псевдоним таблицы задают через КАК: Справочник.Номенклатура КАК Номенклатура. К полям обращаются через точку от псевдонима.

Агрегатные функции — СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ. Поля, которые не попали под агрегат, обязаны стоять в СГРУППИРОВАТЬ ПО:

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

Условие на уже посчитанный агрегат ставят не в ГДЕ, а в ИМЕЮЩИЕ (аналог HAVING). Например, оставить только контрагентов с суммой больше 100 000.

Отбор по значению снаружи передают параметром. В тексте пишут &ИмяПараметра, в коде — заполняют:

Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ТекущаяДата()));

Никогда не склеивайте значения отбора прямо в текст запроса строкой — теряется кэш плана и возникают ошибки типов. Только через УстановитьПараметр.

Полезные операторы отбора: В (список значений), В ИЕРАРХИИ (отбор по группе справочника со всеми вложенными), МЕЖДУ, ПОДОБНО (поиск по шаблону с %). Дубли убирает ВЫБРАТЬ РАЗЛИЧНЫЕ, ограничение количества строк — ВЫБРАТЬ ПЕРВЫЕ 10.

Условную логику прямо в запросе задаёт конструкция ВЫБОР КОГДА … ТОГДА … ИНАЧЕ … КОНЕЦ (аналог CASE). Удобно, когда одно поле надо вычислить по условию, не выходя в код:

ВЫБОР
    КОГДА Остатки.КоличествоОстаток > 0 ТОГДА "В наличии"
    ИНАЧЕ "Нет на складе"
КОНЕЦ КАК Статус

Соединения таблиц: как связать документы и справочники

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

СоединениеЧто вернёт
ЛЕВОЕ СОЕДИНЕНИЕвсе строки левой таблицы, из правой — совпавшие или NULL
ВНУТРЕННЕЕ СОЕДИНЕНИЕтолько пары, где совпадение есть в обеих
ПРАВОЕ СОЕДИНЕНИЕзеркало левого
ПОЛНОЕ СОЕДИНЕНИЕвсе строки обеих таблиц

На практике в 90% случаев берут ЛЕВОЕ СОЕДИНЕНИЕ — основная таблица слева, к ней подтягиваются дополнительные данные:

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

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

При левом соединении несовпавшие поля приходят как NULL. Их обрабатывают функцией ЕСТЬNULL(Поле, ЗначениеПоУмолчанию) — иначе арифметика с NULL даст NULL, а не ноль.

Временные таблицы и пакетные запросы

Когда логика сложная, один гигантский запрос с вложенными подзапросами читается плохо и работает медленно. Разбейте его на шаги через временные таблицы.

Временная таблица создаётся оператором ПОМЕСТИТЬ ИмяТаблицы, а дальше используется как обычная:

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

Точка с запятой ; разделяет запросы в пакете. Такой пакетный запрос выполняется одним вызовом Выполнить(), но внутри идёт по шагам.

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

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

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

Строки разных выборок складывают оператором ОБЪЕДИНИТЬ. Он убирает дубли; если дубли не мешают (а чаще так и есть) — берите ОБЪЕДИНИТЬ ВСЕ, это заметно быстрее, потому что платформе не нужно искать и отсеивать одинаковые строки.

Виртуальные таблицы регистров: остатки и обороты

Главное богатство 1С — регистры, и к ним обращаются не напрямую, а через виртуальные таблицы. Платформа сама собирает по ним агрегаты, поэтому считать остатки «вручную» суммированием движений не нужно.

Основные виртуальные таблицы:

  • 📊 РегистрНакопления.<Имя>.Остатки — остаток на дату (товары на складе, деньги);
  • 📊 РегистрНакопления.<Имя>.Обороты — приход/расход за период;
  • 📊 РегистрНакопления.<Имя>.ОстаткиИОбороты — и то, и другое разом;
  • 📊 РегистрСведений.<Имя>.СрезПоследних — актуальное значение (например, цена или курс на дату);
  • 📊 РегистрБухгалтерии.<Имя>.Остатки и .Обороты — сальдо и обороты по счетам.

У виртуальной таблицы есть параметры в скобках — период и отбор:

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

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

Система компоновки данных (СКД): когда запроса мало

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

СКД — это надстройка над запросом. Вы задаёте запрос как источник набора данных, а дальше система сама:

  • строит группировки и итоги по уровням;
  • даёт пользователю отборы, сортировки и условное оформление;
  • выводит результат в таблицу, диаграмму или и то и другое.

Схему компоновки создают в объекте Отчёт → макет типа Схема компоновки данных. В конструкторе схемы на вкладке Наборы данных вставляют текст запроса, на вкладке Настройки собирают структуру вывода.

Правило простое: нужен внутренний расчёт в коде — пишите запрос с выборкой. Нужен отчёт для пользователя — делайте на СКД. Переписывать ручной вывод таблицы, когда есть компоновка, — потерянное время.

В запросе СКД поля-ресурсы (то, что суммируется) помечают на отдельной вкладке, а в тексте часто используют фигурные скобки {} — это секции, которыми СКД управляет динамически (какие поля реально выбрать, какие отборы наложить).

Оптимизация: как не положить базу тяжёлым запросом

Медленный запрос — это почти всегда не «мало памяти», а неправильно написанный текст. Разберём, что ускоряет, а что убивает производительность.

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

Проверять скорость нужно на боевых объёмах. Запрос, мгновенный на демо-базе из ста строк, на базе с миллионами движений ведёт себя иначе. Смотрите план через технологический журнал или замер производительности в режиме отладки.

Обработка результата и типичные ошибки

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

  • Результат.Выбрать() — линейная выборка, обход циклом Пока … Следующий();
  • Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам) — иерархический обход по группировкам;
  • Результат.Выгрузить() — сразу таблица значений;
  • Результат.Пустой() — быстрая проверка, вернул ли запрос хоть что-то.

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

Если в запросе нужны итоги по уровням (например, сумма по контрагенту и общий итог), добавьте в конце текста секцию ИТОГИ СУММА(Сумма) ПО Контрагент. Тогда результат обходят через ОбходРезультатаЗапроса.ПоГруппировкам, и платформа сама вернёт строки итогов отдельными уровнями — считать их в коде не нужно.

Частые ошибки, на которых спотыкаются даже опытные:

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

Ещё одна частая претензия платформы — обращение к полю, которого нет в секции ВЫБРАТЬ при упорядочивании или группировке. Правило: всё, что сортируете и группируете, должно быть доступно в результате.

Проверьте, как вы поняли логику языка запросов:

Проверьте себя
Чем `ОБЪЕДИНИТЬ ВСЕ` отличается от `ОБЪЕДИНИТЬ`?
Где правильно ставить отбор для таблицы остатков?
Можно ли запросом 1С изменить данные в базе?

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

Чем язык запросов 1С отличается от SQL?
Синтаксис похож, но это отдельный язык: ключевые слова на русском, нет операций изменения данных (только чтение), зато есть обращение к данным через точку, виртуальные таблицы регистров и работа с иерархией справочников. Платформа переводит запрос в SQL сама.
Как передать в запрос список значений для отбора?
Создайте массив или список значений в коде и передайте через УстановитьПараметр, а в тексте используйте оператор В (&СписокЗначений). Платформа подставит весь список.
Почему запрос быстрый на тестовой базе и медленный на рабочей?
На маленькой базе план выполнения неважен — данных мало. На больших объёмах всплывают декартовы соединения, отборы в ГДЕ вместо параметров и получение через точку. Тестируйте на реальном объёме.
Когда делать отчёт на СКД, а когда писать выборку в коде?
Выборка — для внутренних расчётов, проведения, обработок. СКД — когда результат видит пользователь и ему нужны настройки, группировки и итоги. Для отчётов почти всегда выбирают компоновку.
Можно ли в одном запросе обратиться к нескольким временным таблицам?
Да. В пакетном запросе (запросы через ;) каждая временная таблица доступна в последующих запросах пакета. Главное — задать запросу менеджер временных таблиц.