Можно ли передавать имена колонок в запрос 1С через параметры?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок часто возникает задача динамического изменения состава колонок или измерений группировки. Попытка передать имя поля в секцию ВЫБРАТЬ через параметр вида ВЫБРАТЬ Таб.&Параметр1 ИЗ ... приводит к синтаксической ошибке. Разберем, почему платформа 1С накладывает такое ограничение, и подробно рассмотрим все доступные способы решения этой задачи.

Почему параметры нельзя использовать для имен полей?

Проанализируем принцип работы языка запросов 1С. Механизм передачи параметров через знак амперсанда (&) предназначен исключительно для типизированных литералов и значений данных. Они передаются в СУБД в готовые выражения сравнения, условия секции ГДЕ, параметры виртуальных таблиц или в конструкции ВЫБОР.

Имена таблиц, полей и псевдонимов являются структурными идентификаторами синтаксиса. Парсер запросов 1С и оптимизатор СУБД должны однозначно знать структуру выборки на этапе синтаксического анализа и построения плана выполнения. Подставить имя колонки как значение параметра на уровне парсера платформы невозможно ни в одной версии 1С.

Способ 1. Использование условной конструкции ВЫБОР с NULL или НЕОПРЕДЕЛЕНО

Если требуется оставить текст запроса статичным, но отключать определенные измерения для группировки в зависимости от настроек, применим конструкцию ВЫБОР с управляющим булевым параметром.

Рассмотрим пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВЫБОР
    |		КОГДА &НужнаОрганизация ТОГДА Продажи.Организация
    |		ИНАЧЕ NULL
    |	КОНЕЦ КАК Организация,
    |	ВЫБОР
    |		КОГДА &НуженКонтрагент ТОГДА Продажи.Контрагент
    |		ИНАЧЕ NULL
    |	КОНЕЦ КАК Контрагент,
    |	СУММА(Продажи.Сумма) КАК Сумма
    |ИЗ
    |	РегистрНакопления.Продажи КАК Продажи
    |СГРУППИРОВАТЬ ПО
    |	ВЫБОР
    |		КОГДА &НужнаОрганизация ТОГДА Продажи.Организация
    |		ИНАЧЕ NULL
    |	КОНЕЦ,
    |	ВЫБОР
    |		КОГДА &НуженКонтрагент ТОГДА Продажи.Контрагент
    |		ИНАЧЕ NULL
    |	КОНЕЦ";

Запрос.УстановитьПараметр("НужнаОрганизация", Истина);
Запрос.УстановитьПараметр("НуженКонтрагент", Ложь);

Результат = Запрос.Выполнить();

Разберем особенности этого подхода:

  1. Когда параметр отключен (значение Ложь), для всех строк возвращается значение NULL, и данные сворачиваются в одну общую группу.
  2. Поле физически остается в выборке и в секции группировки СУБД, что может снижать производительность на больших объемах данных из-за невозможности эффективного использования индексов.
  3. При выводе результата в интерфейс или табличный документ потребуется программная проверка на NULL, чтобы скрыть лишние колонки.

Способ 2. Динамическая сборка текста запроса в коде

Наиболее чистый с точки зрения производительности СУБД вариант — сформировать текст запроса непосредственно во встроенном языке перед его выполнением. Посмотрим на практический пример сборки текста с использованием функции СтрШаблон:


МассивПолей = Новый Массив;
Если НужнаОрганизация Тогда
    МассивПолей.Добавить("Продажи.Организация КАК Организация");
КонецЕсли;

Если НуженКонтрагент Тогда
    МассивПолей.Добавить("Продажи.Контрагент КАК Контрагент");
КонецЕсли;

МассивПолей.Добавить("СУММА(Продажи.Сумма) КАК Сумма");

СписокПолей = СтрСоединить(МассивПолей, ", ");

МассивГруппировок = Новый Массив;
Если НужнаОрганизация Тогда
    МассивГруппировок.Добавить("Продажи.Организация");
КонецЕсли;
Если НуженКонтрагент Тогда
    МассивГруппировок.Добавить("Продажи.Контрагент");
КонецЕсли;

СекцияГруппировки = "";
Если МассивГруппировок.Количество() > 0 Тогда
    СекцияГруппировки = "СГРУППИРОВАТЬ ПО " + СтрСоединить(МассивГруппировок, ", ");
КонецЕсли;

ШаблонЗапроса = 
    "ВЫБРАТЬ
    |	%1
    |ИЗ
    |	РегистрНакопления.Продажи КАК Продажи
    |%2";

Запрос = Новый Запрос;
Запрос.Текст = СтрШаблон(ШаблонЗапроса, СписокПолей, СекцияГруппировки);
Результат = Запрос.Выполнить();

Способ 3. Использование объекта «ПостроительЗапроса»

Если требуется избежать ручной склейки строк, применим ПостроительЗапроса и специальное расширение языка запросов в фигурных скобках {}. Этот механизм поддерживается платформой, начиная с версии 8.0.


Построитель = Новый ПостроительЗапроса;
Построитель.Текст = 
    "ВЫБРАТЬ
    |	{Продажи.Организация КАК Организация,
    |	Продажи.Контрагент КАК Контрагент,
    |	Продажи.Номенклатура КАК Номенклатура,}
    |	СУММА(Продажи.Сумма) КАК Сумма
    |ИЗ
    |	РегистрНакопления.Продажи КАК Продажи
    |{СГРУППИРОВАТЬ ПО
    |	Продажи.Организация,
    |	Продажи.Контрагент,
    |	Продажи.Номенклатура}";

// Включаем только необходимые поля для выборки
ПолеОрг = Построитель.ВыбранныеПоля.Добавить("Организация");
ПолеКонтр = Построитель.ВыбранныеПоля.Добавить("Контрагент");

// Платформа сама сгенерирует результирующий SQL-запрос только с активными полями
Построитель.Выполнить();
Результат = Построитель.Результат;

Способ 4. Программная модификация через объект «СхемаЗапроса»

В современных версиях платформы (начиная с 8.2.14 и в 8.3) для безопасной динамической модификации запросов без манипуляций со строками используется объект СхемаЗапроса. Он разбирает текст запроса в объектную модель:


Схема = Новый СхемаЗапроса;
Схема.УстановитьТекст(
    "ВЫБРАТЬ
    |	СУММА(Продажи.Сумма) КАК Сумма
    |ИЗ
    |	РегистрНакопления.Продажи КАК Продажи"
);

Оператор = Схема.ПакетЗапросов[0].Операторы[0];

// Программно добавляем колонку и группировку
Если НужнаОрганизация Тогда
    Оператор.ВыбираемыеПоля.Добавить("Продажи.Организация", 0);
    Оператор.Группировка.Добавить("Продажи.Организация");
КонецЕсли;

Запрос = Новый Запрос;
Запрос.Текст = Схема.ПолучитьТекст();
Результат = Запрос.Выполнить();

Способ 5. Использование Системы компоновки данных (СКД)

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

← На главную