Запросы 1С на практике: от консоли до плана выполнения
Содержание
Выполнить запрос в 1С 8.3 можно тремя способами: в обработке «Консоль запросов» в режиме 1С:Предприятие, в отладчике конфигуратора через «Вычислить выражение» (Shift+F9) и из кода модуля через объект Запрос. Для проверки данных и черновиков удобнее консоль, для боевого кода — модуль, для поиска ошибки в чужом коде — отладчик.
Рабочий порядок у разработчиков обычно такой: текст запроса отлаживают в консоли, переносят в модуль с параметрами и проверяют скорость на реальном объёме данных. Запрос в 1С только читает данные, поэтому его выполнение безопасно для базы, пока вы не запускаете вместе с ним код записи.
Где запустить запрос: консоль, отладчик и модуль
Консоль запросов
В типовых конфигурациях встроенной консоли нет. Её берут в виде внешней обработки из комплекта стандартных обработок, который распространяется через ИТС. Для интерфейса «Такси» нужна версия на управляемых формах.
Как открыть:
- Запустите базу в режиме 1С:Предприятие под пользователем с правом открывать внешние обработки.
- Откройте главное меню (кнопка с тремя полосками в заголовке окна) →
Файл → Открыть. - Выберите файл обработки консоли (
.epf). - В верхнее поле вставьте текст запроса, задайте параметры и нажмите кнопку выполнения.
Консоль работает с той базой, в которой открыта. Не проверяйте запросы на изменение данных в рабочей базе: консоль читает данные, но многие версии умеют выполнять и произвольный код. Черновики отлаживайте на копии.
💡 Если в базе нет права на открытие внешних обработок, консоль можно подключить через Администрирование → Печатные формы, отчеты и обработки → Дополнительные отчеты и обработки, если в конфигурации включено их использование.
Отладчик конфигуратора
Это приём, который часто упускают. Когда нужно понять, что возвращает запрос внутри работающей процедуры, консоль не поможет: там другие параметры и другое состояние.
- В конфигураторе поставьте точку останова (
F9) на строке послеЗапрос.Выполнить(). - Запустите отладку (
F5) и выполните действие, которое вызывает этот код. - Когда выполнение остановится, нажмите
Shift+F9и введите выражение:
Запрос.Выполнить().Выгрузить()
Отладчик покажет таблицу значений с результатом. Так же можно посмотреть параметры: Запрос.Параметры и сам текст Запрос.Текст.
Код модуля
Минимальный рабочий шаблон для общего модуля или модуля формы на сервере:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Контрагент,
| Контрагенты.ИНН КАК ИНН
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| НЕ Контрагенты.ПометкаУдаления
| И Контрагенты.ИНН = &ИНН";
Запрос.УстановитьПараметр("ИНН", "7701234567");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Контрагент);
КонецЦикла;
Запросы выполняются только на сервере. В клиентском модуле управляемой формы вызов Новый Запрос вызовет ошибку: вынесите код в процедуру с директивой &НаСервере или в серверный общий модуль.
Как устроен текст запроса 1С
Язык запросов 1С похож на SQL, но обращается не к таблицам СУБД, а к объектам метаданных: Справочник.Номенклатура, Документ.РеализацияТоваровУслуг, РегистрНакопления.ТоварыНаСкладах. Ключевые слова пишут по-русски или по-английски, смешивать можно, но лучше держать один стиль.
Порядок разделов в одном запросе:
| Раздел | Назначение |
|---|---|
ВЫБРАТЬ | Список полей, РАЗЛИЧНЫЕ, ПЕРВЫЕ N |
ПОМЕСТИТЬ | Сохранить результат во временную таблицу |
ИЗ | Источники и соединения |
ГДЕ | Отбор строк до группировки |
СГРУППИРОВАТЬ ПО | Группировка для СУММА, КОЛИЧЕСТВО и т.п. |
ИМЕЮЩИЕ | Отбор по агрегатам после группировки |
УПОРЯДОЧИТЬ ПО | Сортировка |
ИТОГИ ... ПО | Итоги для иерархического обхода |
Несколько вещей, которые отличают язык запросов 1С от обычного SQL:
- 🔗 Разыменование через точку.
Реализация.Контрагент.ИННработает без явного соединения, но платформа сама добавит соединение с таблицей контрагентов. Для составного типа поля таких соединений будет по числу типов. - 📦 Виртуальные таблицы регистров.
Остатки,Обороты,ОстаткиИОбороты,СрезПоследнихпринимают параметры в скобках: период и условие. - 🧩 Конструктор запроса. В конфигураторе поставьте курсор внутрь строки с текстом запроса и вызовите из контекстного меню
Конструктор запроса. Он соберёт текст с вкладками таблиц, связей, условий и группировок. В консоли запросов конструктор тоже доступен. - ⚪ NULL. Поле из несоединившейся таблицы получает
NULL. Сравнение сNULLчерез=не работает : используйтеЕСТЬ NULLили функциюЕСТЬNULL(Поле, 0).
Пример запроса к остаткам с параметрами виртуальной таблицы:
ВЫБРАТЬ
Остатки.Номенклатура,
Остатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&НаДату, Склад = &Склад) КАК Остатки
Имя регистра и его измерения зависят от конфигурации. Смотрите их в дереве метаданных или на вкладке таблиц конструктора.
Параметры запроса и временные таблицы
Как передавать параметры
Значения в текст запроса не вклеивают строкой. Вместо этого пишут &ИмяПараметра и задают значение методом УстановитьПараметр. Так платформа сама приводит типы, а текст запроса не ломается от кавычек в значении.
Что можно передать параметром:
- 📅 дату и границу периода (
Новый Граница(Дата, ВидГраницы.Включая)); - 🔗 ссылку на элемент справочника или документ;
- 📋 массив или список значений — для условия
В (&Список); - 📊 таблицу значений — для выборки из неё через временную таблицу.
Для конца дня удобно передавать КонецДня(Дата): иначе документ, проведённый в 15:00, не попадёт в отбор «по дату включительно», если параметр равен полуночи.
Временные таблицы и пакеты
Сложный запрос проще и быстрее разбить на шаги. Каждый шаг кладёт результат во временную таблицу через ПОМЕСТИТЬ, следующий шаг её читает. Запросы пакета разделяются точкой с запятой.
ВЫБРАТЬ
Продажи.Номенклатура,
СУММА(Продажи.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Продажи
ГДЕ
Продажи.Ссылка.Дата МЕЖДУ &Начало И &Конец
И Продажи.Ссылка.Проведен
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
////////////////////////////////////////
ВЫБРАТЬ
ВТ_Продажи.Номенклатура,
ВТ_Продажи.Количество
ИЗ
ВТ_Продажи КАК ВТ_Продажи
УПОРЯДОЧИТЬ ПО
Количество УБЫВ
Имя табличной части документа отличается в разных конфигурациях — сверяйтесь с метаданными.
Как это выполнить из кода:
Запрос.Выполнить()вернёт результат последнего запроса пакета;Запрос.ВыполнитьПакет()вернёт массив результатов всех запросов по порядку;ВыполнитьПакетСПромежуточнымиДанными()дополнительно отдаёт содержимое временных таблиц — удобно при отладке.
Если временные таблицы должны жить между несколькими запросами, создайте Новый МенеджерВременныхТаблиц и присвойте его свойству Запрос.МенеджерВременныхТаблиц у каждого запроса. Когда таблица больше не нужна, удалите её командой УНИЧТОЖИТЬ ВТ_Продажи или закройте менеджер методом Закрыть().
💡 Таблицу значений можно передать параметром и выбрать из неё: ВЫБРАТЬ * ПОМЕСТИТЬ ВТ ИЗ &Таблица КАК Т. У колонок таблицы значений должны быть заданы типы, иначе запрос упадёт.
Соединения и объединения таблиц в запросе
Соединение добавляет колонки из другой таблицы, объединение добавляет строки. Путают их часто, поэтому сначала таблица.
| Конструкция | Что даёт | Когда брать |
|---|---|---|
ЛЕВОЕ СОЕДИНЕНИЕ | Все строки левой таблицы + совпавшие справа, иначе NULL | Товары и их цены, если цены есть не у всех |
ВНУТРЕННЕЕ СОЕДИНЕНИЕ | Только строки, нашедшие пару | Нужны только позиции, у которых есть и продажи, и остаток |
ПРАВОЕ СОЕДИНЕНИЕ | Зеркало левого | Почти не используют: проще поменять таблицы местами |
ПОЛНОЕ СОЕДИНЕНИЕ | Все строки обеих таблиц | Сверка двух источников: что есть только слева, только справа |
ОБЪЕДИНИТЬ ВСЕ | Строки обоих запросов подряд | Приход и расход в одну таблицу |
ОБЪЕДИНИТЬ | То же без дублей | Когда повторы действительно не нужны |
Правила, которые экономят часы отладки:
- Условие правой таблицы — в
ПО, а не вГДЕ. Если при левом соединении написатьГДЕ Цены.ВидЦены = &ВидЦены, строки без цены отсекутся, и соединение станет по факту внутренним. - Соединение «один ко многим» размножает строки. Соединили шапку документа с табличной частью товаров и сложили сумму документа — сумма умножится на число строк. Группируйте до соединения, во временной таблице.
ОБЪЕДИНИТЬ ВСЕбыстрее, чемОБЪЕДИНИТЬ. БезВСЕСУБД ищет и убирает дубли, на больших выборках это заметно.- В объединении количество и порядок полей должны совпадать, а имена и типы задаёт первый запрос. Пустое поле во втором запросе пишите как
0илиNULL КАК Поле.
Почему запрос выполняется медленно и как это проверить
Замер производительности
Первый шаг — найти, какая строка кода тормозит. В конфигураторе во время отладки включите Отладка → Замер производительности, выполните действие в базе и выключите замер. В таблице замера видно, сколько раз вызывалась каждая строка и какой процент времени она заняла. Строка с Запрос.Выполнить() наверху списка — ваш кандидат.
План выполнения запроса
Платформа превращает запрос 1С в SQL-запрос к СУБД, и решает судьбу скорости именно план этого SQL. Получить его можно через технологический журнал. Файл logcfg.xml кладут в папку conf каталога установленной платформы, например:
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\techlog" history="2">
<event>
<eq property="name" value="dbmssql"/>
</event>
<property name="all"/>
</log>
<plansql/>
</config>
Для PostgreSQL вместо dbmssql указывают событие dbpostgrs. В журнал попадут текст SQL-запроса, длительность и план. Для файловой базы план не выводится: там нет внешней СУБД.
Технологический журнал с полным набором свойств быстро растёт и нагружает диск сервера. Включайте его на короткое время, с отбором по базе или длительности, и удаляйте logcfg.xml после замера.
В плане ищите полное сканирование больших таблиц (Table Scan, Seq Scan) там, где ожидали поиск по индексу.
Что обычно ускоряет запрос
- 🎯 Отбор в параметрах виртуальной таблицы.
Остатки(&Дата, Склад = &Склад)вместоГДЕ Остатки.Склад = &Склад— платформа отфильтрует данные до расчёта остатков. - 🔗 Меньше разыменований составных полей.
Регистратор.Датау регистра с десятком видов документов даст десяток соединений. ИспользуйтеВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг), если тип известен. - 🗂 Временная таблица с индексом вместо подзапроса в соединении:
ИНДЕКСИРОВАТЬ ПОпо полю, по которому дальше идёт соединение. - 🚫 Нет запросов в цикле. Сто вызовов
Запрос.Выполнить()по одной ссылке медленнее одного запроса с условиемВ (&Массив). - ✂️
ПЕРВЫЕ Nпри проверке. Пока отлаживаете текст в консоли, ограничьте выборку, чтобы не ждать миллион строк.
Ошибки при выполнении запросов и способы отладки
Текст ошибки платформа выводит с номером строки и позицией в тексте запроса, а место ошибки отмечает символами <<?>>. Начинайте разбор с этой позиции.
Порядок отладки сложного запроса
- Скопируйте текст запроса из отладчика (
Запрос.Текст) и значения параметров. - Вставьте его в консоль и задайте те же параметры.
- Выполняйте пакет по частям: оставьте первые запросы и посмотрите содержимое временных таблиц.
- Найдя шаг, где данные расходятся с ожиданием, проверьте его соединения и условия по таблице из раздела выше.
- Исправленный текст верните в модуль и проверьте замером производительности.
💡 Ошибку прав доступа запрос в консоли может не показать, если вы работаете под администратором. Пользователь с ограничениями на уровне записей (RLS) получит другой результат или ошибку. Проверяйте под той же учётной записью, у которой проблема, либо добавьте в запрос РАЗРЕШЕННЫЕ после ВЫБРАТЬ, чтобы недоступные записи отбрасывались, а не вызывали ошибку.