Как работать с запросами в 1С 8.3: от синтаксиса до ускорения
Содержание
Запрос в 1С — это не SQL напрямую к базе, а свой язык, который платформа транслирует в SQL сама. Из-за этого частая ошибка новичка — тянуть в запрос UPDATE или DELETE: язык запросов 1С умеет только читать данные, менять их нельзя. Писать и работать с запросами проще, чем кажется: текст набирается в Конфигураторе или через Конструктор, выполняется в коде, результат разбирается выборкой.
Ниже — как устроен синтаксис запроса, чем соединять таблицы, когда брать временные и виртуальные таблицы, зачем нужна система компоновки данных и как не получить запрос, который кладёт базу на колени. Пути и команды — для 1С:Предприятие 8.3.
С чего начинается запрос: конструктор и выполнение в коде
Текст запроса пишут в модуле как обычную строку. Чтобы не набирать руками, в редакторе кода встаньте на пустую строку, нажмите правую кнопку → Конструктор запроса. Откроется визуальный построитель: слева дерево таблиц конфигурации, перетаскиваете нужные поля — конструктор сам соберёт текст.
Минимальный рабочий код выглядит так:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование,
| Номенклатура.Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Наименование);
КонецЦикла;
Три шага: создать объект Запрос, задать Текст, вызвать Выполнить(). Дальше — получить выборку и обойти её циклом Пока Выборка.Следующий().
Вертикальная черта | в начале строк — не часть запроса, а продолжение строковой константы в многострочном тексте. Конструктор ставит её сам.
Если результат нужен сразу таблицей, а не перебором, используйте Результат.Выгрузить() — получите таблицу значений с теми же колонками.
Базовый синтаксис: ВЫБРАТЬ, ИЗ, ГДЕ и группировки
Скелет любого запроса — пять секций, строго в этом порядке:
- 🔹
ВЫБРАТЬ— какие поля вернуть; - 🔹
ИЗ— из какой таблицы (справочник, документ, регистр); - 🔹
ГДЕ— условие отбора строк; - 🔹
СГРУППИРОВАТЬ ПО— свёртка строк с агрегатами; - 🔹
УПОРЯДОЧИТЬ ПО— сортировка результата.
Псевдоним таблицы задают через КАК: Справочник.Номенклатура КАК Номенклатура. К полям обращаются через точку от псевдонима.
Агрегатные функции — СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ. Поля, которые не попали под агрегат, обязаны стоять в СГРУППИРОВАТЬ ПО:
ВЫБРАТЬ
Продажи.Контрагент,
СУММА(Продажи.Сумма) КАК ВсегоПродаж
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Контрагент
Условие на уже посчитанный агрегат ставят не в ГДЕ, а в ИМЕЮЩИЕ (аналог HAVING). Например, оставить только контрагентов с суммой больше 100 000.
Отбор по значению снаружи передают параметром. В тексте пишут &ИмяПараметра, в коде — заполняют:
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ТекущаяДата()));
Никогда не склеивайте значения отбора прямо в текст запроса строкой — теряется кэш плана и возникают ошибки типов. Только через УстановитьПараметр.
Полезные операторы отбора: В (список значений), В ИЕРАРХИИ (отбор по группе справочника со всеми вложенными), МЕЖДУ, ПОДОБНО (поиск по шаблону с %). Дубли убирает ВЫБРАТЬ РАЗЛИЧНЫЕ, ограничение количества строк — ВЫБРАТЬ ПЕРВЫЕ 10.
Условную логику прямо в запросе задаёт конструкция ВЫБОР КОГДА … ТОГДА … ИНАЧЕ … КОНЕЦ (аналог CASE). Удобно, когда одно поле надо вычислить по условию, не выходя в код:
ВЫБОР
КОГДА Остатки.КоличествоОстаток > 0 ТОГДА "В наличии"
ИНАЧЕ "Нет на складе"
КОНЕЦ КАК Статус
Соединения таблиц: как связать документы и справочники
Данные почти всегда лежат в разных таблицах, и их соединяют по ключу. Типов соединения четыре:
| Соединение | Что вернёт |
|---|---|
ЛЕВОЕ СОЕДИНЕНИЕ | все строки левой таблицы, из правой — совпавшие или NULL |
ВНУТРЕННЕЕ СОЕДИНЕНИЕ | только пары, где совпадение есть в обеих |
ПРАВОЕ СОЕДИНЕНИЕ | зеркало левого |
ПОЛНОЕ СОЕДИНЕНИЕ | все строки обеих таблиц |
На практике в 90% случаев берут ЛЕВОЕ СОЕДИНЕНИЕ — основная таблица слева, к ней подтягиваются дополнительные данные:
ВЫБРАТЬ
Заказы.Ссылка,
Заказы.Контрагент,
Контрагенты.ИНН
ИЗ
Документ.ЗаказПокупателя КАК Заказы
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Заказы.Контрагент = Контрагенты.Ссылка
Самая болезненная ошибка — забыть секцию ПО или связать таблицы по неполному условию. Платформа соединит каждую строку с каждой (декартово произведение), и вместо тысячи строк вернутся миллионы. Запрос зависнет, а база — встанет.
При левом соединении несовпавшие поля приходят как NULL. Их обрабатывают функцией ЕСТЬNULL(Поле, ЗначениеПоУмолчанию) — иначе арифметика с NULL даст NULL, а не ноль.
Временные таблицы и пакетные запросы
Когда логика сложная, один гигантский запрос с вложенными подзапросами читается плохо и работает медленно. Разбейте его на шаги через временные таблицы.
Временная таблица создаётся оператором ПОМЕСТИТЬ ИмяТаблицы, а дальше используется как обычная:
ВЫБРАТЬ
Товары.Номенклатура,
СУММА(Товары.Количество) КАК Количество
ПОМЕСТИТЬ ВтТовары
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
СГРУППИРОВАТЬ ПО
Товары.Номенклатура
;
ВЫБРАТЬ
ВтТовары.Номенклатура,
ВтТовары.Количество
ИЗ
ВтТовары КАК ВтТовары
Точка с запятой ; разделяет запросы в пакете. Такой пакетный запрос выполняется одним вызовом Выполнить(), но внутри идёт по шагам.
Чтобы временные таблицы жили, запросу нужен менеджер временных таблиц:
МВТ = Новый МенеджерВременныхТаблиц;
Запрос.МенеджерВременныхТаблиц = МВТ;
Через один менеджер несколько запросов видят общие временные таблицы — удобно, когда расчёт собирается из нескольких процедур.
Строки разных выборок складывают оператором ОБЪЕДИНИТЬ. Он убирает дубли; если дубли не мешают (а чаще так и есть) — берите ОБЪЕДИНИТЬ ВСЕ, это заметно быстрее, потому что платформе не нужно искать и отсеивать одинаковые строки.
Виртуальные таблицы регистров: остатки и обороты
Главное богатство 1С — регистры, и к ним обращаются не напрямую, а через виртуальные таблицы. Платформа сама собирает по ним агрегаты, поэтому считать остатки «вручную» суммированием движений не нужно.
Основные виртуальные таблицы:
- 📊
РегистрНакопления.<Имя>.Остатки— остаток на дату (товары на складе, деньги); - 📊
РегистрНакопления.<Имя>.Обороты— приход/расход за период; - 📊
РегистрНакопления.<Имя>.ОстаткиИОбороты— и то, и другое разом; - 📊
РегистрСведений.<Имя>.СрезПоследних— актуальное значение (например, цена или курс на дату); - 📊
РегистрБухгалтерии.<Имя>.Остаткии.Обороты— сальдо и обороты по счетам.
У виртуальной таблицы есть параметры в скобках — период и отбор:
ВЫБРАТЬ
Остатки.Номенклатура,
Остатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатка, Склад = &Склад) КАК Остатки
Отбор ставьте именно в параметрах виртуальной таблицы, а не в секции ГДЕ. В параметрах платформа отсечёт лишние данные ещё до расчёта агрегата — это и есть главный приём оптимизации работы с регистрами. Если тот же отбор повесить в ГДЕ, система сначала посчитает остатки по всем складам, а потом отбросит ненужные.
Система компоновки данных (СКД): когда запроса мало
Если результат запроса нужно показать пользователю отчётом — с группировками, итогами, настройками, которые он сам крутит, — пишут не выборку в цикле, а отчёт на системе компоновки данных.
СКД — это надстройка над запросом. Вы задаёте запрос как источник набора данных, а дальше система сама:
- строит группировки и итоги по уровням;
- даёт пользователю отборы, сортировки и условное оформление;
- выводит результат в таблицу, диаграмму или и то и другое.
Схему компоновки создают в объекте Отчёт → макет типа Схема компоновки данных. В конструкторе схемы на вкладке Наборы данных вставляют текст запроса, на вкладке Настройки собирают структуру вывода.
Правило простое: нужен внутренний расчёт в коде — пишите запрос с выборкой. Нужен отчёт для пользователя — делайте на СКД. Переписывать ручной вывод таблицы, когда есть компоновка, — потерянное время.
В запросе СКД поля-ресурсы (то, что суммируется) помечают на отдельной вкладке, а в тексте часто используют фигурные скобки {} — это секции, которыми СКД управляет динамически (какие поля реально выбрать, какие отборы наложить).
Оптимизация: как не положить базу тяжёлым запросом
Медленный запрос — это почти всегда не «мало памяти», а неправильно написанный текст. Разберём, что ускоряет, а что убивает производительность.
- Не используйте
ВЫБРАТЬ *и лишние поля. Берите только то, что реально нужно. Каждое поле — это чтение и передача данных. - Отбор — в параметры виртуальных таблиц, а не в
ГДЕ(см. выше). Это самый частый источник тормозов на регистрах. - Избегайте получения данных через точку от полей составного типа. Запись вида
Движения.Регистратор.Дата, гдеРегистратор— составной тип, заставляет платформу делать левое соединение сразу со всеми таблицами-документами. Лучше получить ссылку, а поля брать отдельным запросом или черезВЫРАЗИТЬ. - Приводите тип через
ВЫРАЗИТЬ(Поле КАК Документ.РеализацияТоваровУслуг)— тогда соединение пойдёт только с одной таблицей. - Заменяйте вложенные подзапросы в условиях на временные таблицы. Соединение с подзапросом платформа часто не может оптимизировать.
- Не соединяйте запрос с виртуальной таблицей напрямую — сначала выгрузите виртуальную таблицу во временную, потом соединяйте.
Проверять скорость нужно на боевых объёмах. Запрос, мгновенный на демо-базе из ста строк, на базе с миллионами движений ведёт себя иначе. Смотрите план через технологический журнал или замер производительности в режиме отладки.
Обработка результата и типичные ошибки
Результат запроса (Запрос.Выполнить()) можно разобрать несколькими способами:
Результат.Выбрать()— линейная выборка, обход цикломПока … Следующий();Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам)— иерархический обход по группировкам;Результат.Выгрузить()— сразу таблица значений;Результат.Пустой()— быстрая проверка, вернул ли запрос хоть что-то.
Проверять Пустой() перед циклом полезно: позволяет сразу выйти, если данных нет, не заходя в обработку.
Если в запросе нужны итоги по уровням (например, сумма по контрагенту и общий итог), добавьте в конце текста секцию ИТОГИ СУММА(Сумма) ПО Контрагент. Тогда результат обходят через ОбходРезультатаЗапроса.ПоГруппировкам, и платформа сама вернёт строки итогов отдельными уровнями — считать их в коде не нужно.
Частые ошибки, на которых спотыкаются даже опытные:
Ещё одна частая претензия платформы — обращение к полю, которого нет в секции ВЫБРАТЬ при упорядочивании или группировке. Правило: всё, что сортируете и группируете, должно быть доступно в результате.
Проверьте, как вы поняли логику языка запросов:
Частые вопросы
Чем язык запросов 1С отличается от SQL?
Как передать в запрос список значений для отбора?
УстановитьПараметр, а в тексте используйте оператор В (&СписокЗначений). Платформа подставит весь список.Почему запрос быстрый на тестовой базе и медленный на рабочей?
ГДЕ вместо параметров и получение через точку. Тестируйте на реальном объёме.Когда делать отчёт на СКД, а когда писать выборку в коде?
Можно ли в одном запросе обратиться к нескольким временным таблицам?
;) каждая временная таблица доступна в последующих запросах пакета. Главное — задать запросу менеджер временных таблиц.