Как передать имя таблицы или регистра в качестве параметра в запросе 1С?

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

В процессе разработки в среде «1С:Предприятие» программисты регулярно сталкиваются с задачей, когда имя таблицы-источника (например, конкретного регистра накопления, сведения или справочника) становится известным только в момент выполнения кода. Естественным желанием разработчика является передача имени таблицы через стандартный механизм параметров запроса, например: Запрос.УстановитьПараметр("ИмяРегистра", Объект.ИмяРегистра).

Однако платформа «1С:Предприятие» накладывает жесткое ограничение: использовать имя таблицы базы данных в качестве стандартного параметра запроса напрямую нельзя. Подобная конструкция вызовет синтаксическую ошибку на этапе компиляции запроса. В этой статье мы подробно разберем, как обойти это ограничение, рассмотрим все доступные методы — от простой строковой замены до профессионального использования «Схемы запроса» и механизмов СКД, а также проанализируем подводные камни каждого подхода.

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

Самый простой и наиболее часто используемый на практике подход — это подмена части строки текста запроса перед его выполнением. Существует несколько вариантов реализации этого метода.

Вариант А. Простая конкатенация строк

Мы можем собрать текст запроса «на лету», используя оператор сложения строк. Рассмотрим пример:


ИмяВыбранногоРегистра = "ОстаткиАлкогольнойПродукцииЕГАИС";

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	Регистр.Регистратор КАК Регистратор
    |ИЗ
    |	РегистрНакопления." + ИмяВыбранногоРегистра + " КАК Регистр
    |ГДЕ
    |	Регистр.Регистратор.Дата < &КонПериода";

Запрос.УстановитьПараметр("КонПериода", Объект.КонПериода);
РезультатЗапроса = Запрос.Выполнить();

Минусы подхода: Такой код тяжело читать, особенно если запрос имеет большой объем. Кроме того, подобный запрос невозможно открыть и отредактировать в стандартном Конструкторе запросов 1С.

Вариант Б. Использование функции СтрЗаменить с шаблонами

Для улучшения читаемости кода мы можем использовать специальные маркеры (шаблоны) внутри текста запроса, которые затем замещаются реальным именем таблицы с помощью функции СтрЗаменить.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	Регистр.Регистратор КАК Регистратор
    |ИЗ
    |	РегистрНакопления.%ИмяРегистра% КАК Регистр
    |ГДЕ
    |	Регистр.Регистратор.Дата < &КонПериода";

// Производим замену маркера на реальное имя таблицы
Запрос.Текст = СтрЗаменить(Запрос.Текст, "%ИмяРегистра%", Объект.ИмяРегистра);
Запрос.УстановитьПараметр("КонПериода", Объект.КонПериода);

Этот вариант значительно чище, однако проблема с редактированием запроса в Конструкторе остается — Конструктор выдаст ошибку на некорректный синтаксис %ИмяРегистра%.

Вариант В. Профессиональный метод сохранения совместимости с Конструктором запросов

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	Регистр.Регистратор КАК Регистратор
    |ИЗ
    |	РегистрНакопления.ОстаткиАлкогольнойПродукцииЕГАИС КАК Регистр //МАРКЕР_ПОДМЕНЫ_ТАБЛИЦЫ
    |ГДЕ
    |	Регистр.Регистратор.Дата < &КонПериода";

// Перед выполнением динамически меняем строку с комментарием на нужную таблицу
СтрокаПоиска = "РегистрНакопления.ОстаткиАлкогольнойПродукцииЕГАИС КАК Регистр //МАРКЕР_ПОДМЕНЫ_ТАБЛИЦЫ";
СтрокаЗамены = "РегистрНакопления." + Объект.ИмяРегистра + " КАК Регистр";

Запрос.Текст = СтрЗаменить(Запрос.Текст, СтрокаПоиска, СтрокаЗамены);
Запрос.УстановитьПараметр("КонПериода", Объект.КонПериода);

При таком подходе разработчик может в любой момент нажать правой кнопкой мыши по тексту запроса в конфигураторе, запустить Конструктор, внести необходимые изменения в связи, поля или условия, и при сохранении логика автозамены не нарушится (главное — сохранить неизменным наш комментарий-маркер).

Способ 2. Использование программного объекта «Схема запроса»

Начиная с платформы 1С 8.3, разработчикам доступен мощный инструмент для объектного анализа и конструирования запросов — СхемаЗапроса. Этот метод исключает работу с «сырым» текстом и строковыми заменами, предоставляя объектную модель запроса.

Выясним, как программно заменить таблицу-источник с помощью схемы запроса:


// Инициализируем схему запроса на основе существующего текста
Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса(ТекстЗапросаИсточника);

// Получаем первый пакет и первый оператор выбора
Пакет = Схема.ПакетЗапросов[0];
ОператорВыбора = Пакет.Операторы[0];

// Удаляем старый источник и добавляем новый
ОператорВыбора.Источники.Очистить();
НовыйИсточник = ОператорВыбора.Источники.Добавить("РегистрНакопления." + Объект.ИмяРегистра, "Регистр");

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

Критически важные особенности и подводные камни СхемыЗапроса:

  1. Разрушение связей и отборов: При очистке коллекции Источники или удалении конкретной таблицы платформа автоматически очистит все связанные с ней соединения (Соединения), выбранные поля и условия в секции ГДЕ. Если структура исходного запроса сложная, вам придется программно обойти все коллекции схемы, запомнить старые связи, а после добавления нового источника — воссоздать их заново.
  2. Работа с Динамическими списками: Если вы модифицируете схему запроса для Динамического списка на управляемой форме, обязательно включайте специальный режим компоновки перед получением текста: Схема.РежимКомпоновкиДанных = Истина; Без активации этого свойства стандартные конструкции системы компоновки данных (например, необязательные отборы в фигурных скобках {ГДЕ ...}) могут некорректно трансформироваться в жесткие условия, что приведет к неработоспособности пользовательских настроек на форме.

Способ 3. Передача внешней таблицы через параметры в секции ИЗ

Хотя передать имя таблицы базы данных в параметр нельзя, платформа позволяет передать готовую внешнюю таблицу (например, ТаблицаЗначений или РезультатЗапроса) в качестве параметра запроса в секции ИЗ.

Для корректного выполнения этой операции необходимо строго соблюдать два правила платформы:

  1. Обязательное помещение во временную таблицу: Вы не можете напрямую делать соединения или выборки из параметра-таблицы. Платформа потребует выгрузить ее во временную таблицу (ВТ) в первом пакете запроса.

    Неправильно:

    ВЫБРАТЬ * ИЗ &ПараметрТаблица КАК Таблица

    Правильно:

    
    ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ПараметрТаблица КАК ПараметрТаблица;
    ////////////////////////////////////////////////////////////////////////////////
    ВЫБРАТЬ * ИЗ ВТ_Данные КАК Данные
    
  2. Строгая типизация колонок таблицы значений: Если вы передаете ТаблицуЗначений, сформированную программно, каждая колонка должна быть строго типизирована с использованием объекта ОписаниеТипов. Если передать нетипизированную колонку, то:
    • Возникнет ошибка выполнения: «Тип не может быть выбран в запросе».
    • В последующих пакетах запроса вы не сможете обратиться к реквизитам полей через точку (например, обращение ВТ_Данные.Номенклатура.Код вернет ошибку «Поле не найдено»).

Рассмотрим пример правильного формирования и передачи таблицы-параметра:


// Создаем типизированную Таблицу Значений
ТЗ = Новый ТаблицаЗначений;

МассивТиповНоменклатура = Новый Массив;
МассивТиповНоменклатура.Добавить(Тип("СправочникСсылка.Номенклатура"));
ОписаниеТиповНоменклатура = Новый ОписаниеТипов(МассивТиповНоменклатура);

ТЗ.Колонки.Добавить("Номенклатура", ОписаниеТиповНоменклатура);
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));

// Заполняем ТЗ данными...
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Номенклатура = ВыбранныйТовар;
НоваяСтрока.Количество = 10;

// Передаем в запрос
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВходнаяТаблица.Номенклатура КАК Номенклатура,
    |	ВходнаяТаблица.Количество КАК Количество
    |ПОМЕСТИТЬ ВТ_ВходныеДанные
    |ИЗ
    |	&ВнешняяТаблица КАК ВходнаяТаблица
    |;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ_ВходныеДанные.Номенклатура.Код КАК КодНоменклатуры,
    |	ВТ_ВходныеДанные.Количество КАК Количество
    |ИЗ
    |	ВТ_ВходныеДанные КАК ВТ_ВходныеДанные";

Запрос.УстановитьПараметр("ВнешняяТаблица", ТЗ);
Результат = Запрос.Выполнить();

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

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

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


СхемаКомпоновки = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
НаборДанных = СхемаКомпоновки.ИсточникиДанных[0]; // Находим нужный набор данных

// Производим замену имени таблицы в запросе набора данных
НаборДанныхЗапрос = СхемаКомпоновки.НаборыДанных.Найти("НаборДанных1");
НаборДанныхЗапрос.Запрос = СтрЗаменить(НаборДанныхЗапрос.Запрос, "РегистрНакопления.Шаблон", "РегистрНакопления." + Объект.ИмяРегистра);

// Дальнейший вывод отчета через ПроцессорКомпоновки...

Способ 5. Объединение таблиц (UNION ALL) с фильтрацией по параметру

В некоторых случаях круг возможных регистров жестко ограничен и известен заранее (например, это могут быть только три конкретных регистра: ОстаткиАлкоголя, ПродажиАлкоголя или СписанияАлкоголя). Тогда мы можем объединить все три таблицы в одном запросе с помощью инструкции ОБЪЕДИНИТЬ ВСЕ, добавив в каждую секцию поле-идентификатор и наложив на него условие.

Посмотрим на пример реализации такого подхода:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	""Остатки"" КАК ИмяТаблицы,
    |	Регистр.Регистратор КАК Регистратор,
    |	Регистр.Номенклатура КАК Номенклатура
    |ИЗ
    |	РегистрНакопления.ОстаткиАлкогольнойПродукцииЕГАИС КАК Регистр
    |ГДЕ
    |	Регистр.Регистратор.Date < &КонПериода
    |
    |ОБЪЕДИНИТЬ ВСЕ
    |
    |ВЫБРАТЬ
    |	""Продажи"" КАК ИмяТаблицы,
    |	Регистр.Регистратор КАК Регистратор,
    |	Регистр.Номенклатура КАК Номенклатура
    |ИЗ
    |	РегистрНакопления.ПродажиАлкогольнойПродукцииЕГАИС КАК Регистр
    |ГДЕ
    |	Регистр.Регистратор.Date < &КонПериода
    |
    |ПОМЕСТИТЬ ВТ_ВсеРегистры
    |;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ_ВсеРегистры.Регистратор,
    |	ВТ_ВсеРегистры.Номенклатура
    |ИЗ
    |	ВТ_ВсеРегистры КАК ВТ_ВсеРегистры
    |ГДЕ
    |	ВТ_ВсеРегистры.ИмяТаблицы = &ВыбранныйРегистр";

Запрос.УстановитьПараметр("КонПериода", Объект.КонПериода);
// Передаем строку "Остатки" или "Продажи"
Запрос.УстановитьПараметр("ВыбранныйРегистр", Объект.ВыбранныйРегистр); 

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

Преимущества метода: Запрос абсолютно статичен, превосходно открывается в Конструкторе запросов, не требует строковых манипуляций и не подвержен риску синтаксических ошибок из-за некорректных символов в имени таблицы.

Недостатки метода: Существенное снижение производительности СУБД, так как СУБД может попытаться просканировать или построить планы выполнения для всех таблиц в блоке объединения, даже если фактически будет выбрана только одна. Метод подходит только для небольших таблиц или редких регламентных операций.

Стандарты разработки 1С и требования БСП

При динамической модификации текстов запросов (особенно в Динамических списках на управляемых формах) стандарты разработки фирмы «1С» и требования Библиотеки Стандартных Подсистем (БСП) предписывают соблюдать следующие правила:

Пример корректной динамической настройки Динамического списка на форме:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	
	// Определяем имя таблицы на основе настроек пользователя или ФО
	ИмяТаблицыРегистра = ПолучитьИмяЦелевогоРегистра();
	
	// Модифицируем текст запроса динамического списка
	СтарыйТекст = Список.ТекстЗапроса;
	НовыйТекст = СтрЗаменить(СтарыйТекст, 
		"РегистрНакопления.ШаблонныйРегистр КАК РегистрПереопределяемый", 
		"РегистрНакопления." + ИмяТаблицыРегистра + " КАК РегистрПереопределяемый");
		
	Список.ТекстЗапроса = НовыйТекст;
	
КонецПроцедуры

Резюме

Мы подробно проанализировали задачу динамической передачи имени таблицы в запрос 1С. Для простых сценариев и небольших отчетов идеально подходит Способ 1 (Вариант В) со строковой заменой конструкции с маркером-комментарием — он обеспечивает гибкость кода и полную совместимость с Конструктором запросов. В сложных архитектурных решениях с динамическими списками лучшим выбором станет использование Схемы запроса (с обязательным учетом нюансов разрушения связей таблиц) или настройка СКД.

← На главную