Запрос в СКД 1С пишется на том же языке запросов, что и обычный Запрос, но выполняется не напрямую. Система компоновки данных берёт текст набора данных как шаблон: добавляет отборы пользователя, убирает неиспользуемые поля и таблицы, подставляет параметры. Поэтому в наборе данных нельзя писать секции ИТОГИ и АВТОУПОРЯДОЧИВАНИЕ, зато можно использовать расширение языка в фигурных скобках {}.

Если коротко: всё, что касается итогов и порядка, переезжает в настройки отчёта, а всё, что должно быть «по желанию пользователя» (параметр может быть не заполнен, отбор может отсутствовать), оформляется фигурными скобками. Ниже разберу, как это работает и как увидеть, во что СКД превратила ваш запрос.

Наборы данных СКД и место запроса среди них

У схемы компоновки три вида наборов данных:

НаборОткуда данныеКогда использовать
ЗапросТаблицы информационной базыПочти всегда: это основной источник
ОбъектТаблица значений или другая коллекция, переданная из кодаДанные, которые нельзя получить запросом: внешние API, расчёты в коде
ОбъединениеНесколько наборов, сложенных как ОБЪЕДИНИТЬ ВСЕСвести разнородные источники в одну таблицу

В девяти отчётах из десяти работает набор «Запрос». Поэтому понимать, чем запрос в СКД отличается от обычного, нужно каждому, кто пишет отчёты.

Чего нельзя в запросе СКД

Два ограничения, о которые спотыкаются чаще всего:

  • Секция ИТОГИ не используется. Итоги в СКД считаются по структуре настроек: группировкам, таблицам, диаграммам. Числовые поля описываются на закладке «Ресурсы» с выражением агрегирования, например Сумма(Количество).
  • АВТОУПОРЯДОЧИВАНИЕ не используется. Порядок задаётся на закладке «Сортировка» в настройках варианта и может меняться пользователем.

Всё остальное доступно: пакетные запросы, временные таблицы, СГРУППИРОВАТЬ ПО, ИМЕЮЩИЕ, ОБЪЕДИНИТЬ, виртуальные таблицы регистров, параметры. Параметры, найденные в тексте запроса, СКД сама добавляет на закладку «Параметры».

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

Автозаполнение: кто описывает поля набора

У набора данных «Запрос» есть флаг «Автозаполнение». Когда он включён:

  1. СКД разбирает текст и заполняет закладку полей набора по последнему пакету.
  2. Сама определяет, какие поля можно использовать в отборах, группировках и сортировке.
  3. Сама решает, в какое место запроса подставить отбор пользователя.

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

Фигурные скобки: расширение языка запросов СКД

Всё, что заключено в {}, — инструкции для компоновщика, а не для СУБД. Краткое описание есть во встроенной справке платформы, раздел про расширение языка запросов для системы компоновки данных. Многие конструкции можно не писать руками, а выставить в конструкторе запроса на закладке «Компоновка данных».

Поля для выбора и отбора

Запрос 1С
ВЫБРАТЬ
	Продажи.Номенклатура КАК Номенклатура,
	Продажи.Контрагент КАК Контрагент,
	Продажи.Сумма КАК Сумма
{ВЫБРАТЬ
	Номенклатура.*,
	Контрагент.*,
	Сумма}
ИЗ
	РегистрНакопления.Продажи КАК Продажи
{ГДЕ
	Продажи.Номенклатура.* КАК Номенклатура,
	Продажи.Контрагент.* КАК Контрагент}

{ВЫБРАТЬ ...} перечисляет поля, которые попадут в набор. Звёздочка после поля разрешает использовать его реквизиты через точку. {ГДЕ ...} перечисляет поля, по которым пользователь может ставить отбор, и показывает компоновщику, куда этот отбор вставлять.

Необязательные условия

Обычное условие с незаполненным параметром даст ошибку при формировании отчёта. Условие в фигурных скобках при незаполненном параметре выбрасывается целиком:

Запрос 1С
ВЫБРАТЬ
	Заказы.Ссылка КАК Заказ,
	Заказы.СуммаДокумента КАК Сумма
ИЗ
	Документ.ЗаказКлиента КАК Заказы
{ГДЕ
	Заказы.Менеджер = &Менеджер,
	Заказы.Дата >= &НачалоПериода}

Если пользователь не указал менеджера, отчёт покажет заказы всех менеджеров. Это удобнее, чем городить в тексте ГДЕ (&Менеджер = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка) ИЛИ Заказы.Менеджер = &Менеджер): такое «ИЛИ» мешает СУБД использовать индекс.

Запрос в СКД 1С: необязательные условия в фигурных скобках включаются только при заполненном параметре
Запрос в СКД 1С: необязательные условия в фигурных скобках включаются только при заполненном параметре

Параметры виртуальных таблиц

Самое полезное место для фигурных скобок — параметры виртуальных таблиц регистров. Отбор, переданный внутрь Остатки() или Обороты(), срабатывает до расчёта таблицы, а не после.

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

{(&ДатаОстатков)} делает параметр необязательным: если он не заполнен, в виртуальную таблицу уйдёт значение по умолчанию (для периода — пустая дата, то есть текущие остатки). Вторая конструкция разрешает пользовательским отборам по складу и номенклатуре попадать прямо в условие виртуальной таблицы.

Характеристики

Расширение языка позволяет подключить к объекту «характеристики» — например, дополнительные реквизиты из плана видов характеристик. Делается это конструкцией {ХАРАКТЕРИСТИКИ ...}, где указываются тип объекта, источник видов характеристик и источник значений. После этого в пользовательском режиме каждая характеристика выглядит как обычный реквизит объекта: по ней можно группировать и отбирать. Конструкцию проще собрать в конструкторе запроса, чем писать вручную, а затем проверить в консоли на реальных данных.

Как СКД преобразует запрос

Текст в наборе данных — не то, что выполнит СУБД. Перед выполнением компоновщик:

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

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

Запрос в СКД 1С: исходный текст набора и итоговый запрос после компоновки макета
Запрос в СКД 1С: исходный текст набора и итоговый запрос после компоновки макета

Получаем итоговый текст запроса кодом

1С
// Возвращает текст запроса, который СКД сформирует для текущих настроек отчета.
&НаСервере
Функция ИтоговыйТекстЗапросаСКД()

	ОтчетОбъект = РеквизитФормыВЗначение("Отчет");
	Схема = ОтчетОбъект.СхемаКомпоновкиДанных;
	Настройки = Отчет.КомпоновщикНастроек.ПолучитьНастройки();

	Компоновщик = Новый КомпоновщикМакетаКомпоновкиДанных;
	Макет = Компоновщик.Выполнить(Схема, Настройки);

	Тексты = Новый Массив;
	Для Каждого НаборДанных Из Макет.НаборыДанных Цикл
		Если ТипЗнч(НаборДанных) = Тип("НаборДанныхЗапросМакетаКомпоновкиДанных") Тогда
			Тексты.Добавить(НаборДанных.Запрос);
		КонецЕсли;
	КонецЦикла;

	Возврат СтрСоединить(Тексты, Символы.ПС + "=====" + Символы.ПС);

КонецФункции

Тот же результат дают консоли компоновки данных: они показывают и исходную схему, и запрос из макета. Если отчёт тормозит, первым делом смотрю именно итоговый текст — по нему сразу видно, где отбор встал «не туда». Системный разбор таких отчётов делаю в рамках аудита производительности 1С.

Язык выражений СКД — не язык запросов

В вычисляемых полях, ресурсах и выражениях параметров используется отдельный язык выражений СКД. Он похож на язык запросов, но выполняется уже после получения данных:

  • простые функции вроде НачалоПериода(Дата, "Месяц");
  • агрегатные Сумма, Количество, Максимум, Среднее для ресурсов;
  • мощные ВычислитьВыражение и ВычислитьВыражениеСГруппировкойМассив, которые позволяют считать доли, нарастающие итоги и значения по другим группировкам.

Правило простое: всё, что можно отфильтровать и сгруппировать на уровне СУБД, делайте в запросе; всё, что зависит от структуры отчёта (процент от итога группы, значение предыдущей строки), — в языке выражений.

Типичные ошибки

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

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

Итог

  • В запросе СКД нет ИТОГИ и АВТОУПОРЯДОЧИВАНИЕ: итоги и порядок задаются настройками отчёта.
  • Поля набора берутся из последнего пакета запроса; при включённом автозаполнении СКД сама описывает поля и места отборов.
  • Фигурные скобки делают параметры и условия необязательными, открывают поля для отбора и позволяют подключить характеристики.
  • Отборы по регистрам выгоднее пускать в параметры виртуальных таблиц.
  • Итоговый текст запроса берите из МакетКомпоновкиДанных.НаборыДанных[...].Запрос или из консоли компоновки.