Запросы 1С на практике: от консоли до плана выполнения

· 10 мин

Содержание

Выполнить запрос в 1С 8.3 можно тремя способами: в обработке «Консоль запросов» в режиме 1С:Предприятие, в отладчике конфигуратора через «Вычислить выражение» (Shift+F9) и из кода модуля через объект Запрос. Для проверки данных и черновиков удобнее консоль, для боевого кода — модуль, для поиска ошибки в чужом коде — отладчик.

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

Где запустить запрос: консоль, отладчик и модуль

Консоль запросов

В типовых конфигурациях встроенной консоли нет. Её берут в виде внешней обработки из комплекта стандартных обработок, который распространяется через ИТС. Для интерфейса «Такси» нужна версия на управляемых формах.

Как открыть:

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

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

💡 Если в базе нет права на открытие внешних обработок, консоль можно подключить через Администрирование → Печатные формы, отчеты и обработки → Дополнительные отчеты и обработки, если в конфигурации включено их использование.

Отладчик конфигуратора

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

  1. В конфигураторе поставьте точку останова (F9) на строке после Запрос.Выполнить().
  2. Запустите отладку (F5) и выполните действие, которое вызывает этот код.
  3. Когда выполнение остановится, нажмите Shift+F9 и введите выражение:
Запрос.Выполнить().Выгрузить()

Отладчик покажет таблицу значений с результатом. Так же можно посмотреть параметры: Запрос.Параметры и сам текст Запрос.Текст.

Код модуля

Минимальный рабочий шаблон для общего модуля или модуля формы на сервере:

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

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	Сообщить(Выборка.Контрагент);
КонецЦикла;

Запросы выполняются только на сервере. В клиентском модуле управляемой формы вызов Новый Запрос вызовет ошибку: вынесите код в процедуру с директивой &НаСервере или в серверный общий модуль.

Как устроен текст запроса 1С

Язык запросов 1С похож на SQL, но обращается не к таблицам СУБД, а к объектам метаданных: Справочник.Номенклатура, Документ.РеализацияТоваровУслуг, РегистрНакопления.ТоварыНаСкладах. Ключевые слова пишут по-русски или по-английски, смешивать можно, но лучше держать один стиль.

Порядок разделов в одном запросе:

РазделНазначение
ВЫБРАТЬСписок полей, РАЗЛИЧНЫЕ, ПЕРВЫЕ N
ПОМЕСТИТЬСохранить результат во временную таблицу
ИЗИсточники и соединения
ГДЕОтбор строк до группировки
СГРУППИРОВАТЬ ПОГруппировка для СУММА, КОЛИЧЕСТВО и т.п.
ИМЕЮЩИЕОтбор по агрегатам после группировки
УПОРЯДОЧИТЬ ПОСортировка
ИТОГИ ... ПОИтоги для иерархического обхода

Несколько вещей, которые отличают язык запросов 1С от обычного SQL:

  • 🔗 Разыменование через точку. Реализация.Контрагент.ИНН работает без явного соединения, но платформа сама добавит соединение с таблицей контрагентов. Для составного типа поля таких соединений будет по числу типов.
  • 📦 Виртуальные таблицы регистров. Остатки, Обороты, ОстаткиИОбороты, СрезПоследних принимают параметры в скобках: период и условие.
  • 🧩 Конструктор запроса. В конфигураторе поставьте курсор внутрь строки с текстом запроса и вызовите из контекстного меню Конструктор запроса. Он соберёт текст с вкладками таблиц, связей, условий и группировок. В консоли запросов конструктор тоже доступен.
  • ⚪ NULL. Поле из несоединившейся таблицы получает NULL. Сравнение с NULL через = не работает : используйте ЕСТЬ NULL или функцию ЕСТЬNULL(Поле, 0).

Пример запроса к остаткам с параметрами виртуальной таблицы:

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

Имя регистра и его измерения зависят от конфигурации. Смотрите их в дереве метаданных или на вкладке таблиц конструктора.

Параметры запроса и временные таблицы

Как передавать параметры

Значения в текст запроса не вклеивают строкой. Вместо этого пишут &ИмяПараметра и задают значение методом УстановитьПараметр. Так платформа сама приводит типы, а текст запроса не ломается от кавычек в значении.

Что можно передать параметром:

  • 📅 дату и границу периода (Новый Граница(Дата, ВидГраницы.Включая));
  • 🔗 ссылку на элемент справочника или документ;
  • 📋 массив или список значений — для условия В (&Список);
  • 📊 таблицу значений — для выборки из неё через временную таблицу.

Для конца дня удобно передавать КонецДня(Дата): иначе документ, проведённый в 15:00, не попадёт в отбор «по дату включительно», если параметр равен полуночи.

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

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

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

Имя табличной части документа отличается в разных конфигурациях — сверяйтесь с метаданными.

Как это выполнить из кода:

  • Запрос.Выполнить() вернёт результат последнего запроса пакета;
  • Запрос.ВыполнитьПакет() вернёт массив результатов всех запросов по порядку;
  • ВыполнитьПакетСПромежуточнымиДанными() дополнительно отдаёт содержимое временных таблиц — удобно при отладке.

Если временные таблицы должны жить между несколькими запросами, создайте Новый МенеджерВременныхТаблиц и присвойте его свойству Запрос.МенеджерВременныхТаблиц у каждого запроса. Когда таблица больше не нужна, удалите её командой УНИЧТОЖИТЬ ВТ_Продажи или закройте менеджер методом Закрыть().

💡 Таблицу значений можно передать параметром и выбрать из неё: ВЫБРАТЬ * ПОМЕСТИТЬ ВТ ИЗ &Таблица КАК Т. У колонок таблицы значений должны быть заданы типы, иначе запрос упадёт.

Соединения и объединения таблиц в запросе

Соединение добавляет колонки из другой таблицы, объединение добавляет строки. Путают их часто, поэтому сначала таблица.

КонструкцияЧто даётКогда брать
ЛЕВОЕ СОЕДИНЕНИЕВсе строки левой таблицы + совпавшие справа, иначе NULLТовары и их цены, если цены есть не у всех
ВНУТРЕННЕЕ СОЕДИНЕНИЕТолько строки, нашедшие паруНужны только позиции, у которых есть и продажи, и остаток
ПРАВОЕ СОЕДИНЕНИЕЗеркало левогоПочти не используют: проще поменять таблицы местами
ПОЛНОЕ СОЕДИНЕНИЕВсе строки обеих таблицСверка двух источников: что есть только слева, только справа
ОБЪЕДИНИТЬ ВСЕСтроки обоих запросов подрядПриход и расход в одну таблицу
ОБЪЕДИНИТЬТо же без дублейКогда повторы действительно не нужны

Правила, которые экономят часы отладки:

  1. Условие правой таблицы — в ПО, а не в ГДЕ. Если при левом соединении написать ГДЕ Цены.ВидЦены = &ВидЦены, строки без цены отсекутся, и соединение станет по факту внутренним.
  2. Соединение «один ко многим» размножает строки. Соединили шапку документа с табличной частью товаров и сложили сумму документа — сумма умножится на число строк. Группируйте до соединения, во временной таблице.
  3. ОБЪЕДИНИТЬ ВСЕ быстрее, чем ОБЪЕДИНИТЬ. Без ВСЕ СУБД ищет и убирает дубли, на больших выборках это заметно.
  4. В объединении количество и порядок полей должны совпадать, а имена и типы задаёт первый запрос. Пустое поле во втором запросе пишите как 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 при проверке. Пока отлаживаете текст в консоли, ограничьте выборку, чтобы не ждать миллион строк.

Ошибки при выполнении запросов и способы отладки

Текст ошибки платформа выводит с номером строки и позицией в тексте запроса, а место ошибки отмечает символами <<?>>. Начинайте разбор с этой позиции.

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

Порядок отладки сложного запроса

  1. Скопируйте текст запроса из отладчика (Запрос.Текст) и значения параметров.
  2. Вставьте его в консоль и задайте те же параметры.
  3. Выполняйте пакет по частям: оставьте первые запросы и посмотрите содержимое временных таблиц.
  4. Найдя шаг, где данные расходятся с ожиданием, проверьте его соединения и условия по таблице из раздела выше.
  5. Исправленный текст верните в модуль и проверьте замером производительности.

💡 Ошибку прав доступа запрос в консоли может не показать, если вы работаете под администратором. Пользователь с ограничениями на уровне записей (RLS) получит другой результат или ошибку. Проверяйте под той же учётной записью, у которой проблема, либо добавьте в запрос РАЗРЕШЕННЫЕ после ВЫБРАТЬ, чтобы недоступные записи отбрасывались, а не вызывали ошибку.

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

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