При разработке алгоритмов проведения или проверки остатков часто возникает необходимость обращаться к различным регистрам накопления в зависимости от реквизитов документа или справочника. Например, в зависимости от вида склада товары могут учитываться на регистрах ТоварыНаСкладах, ТоварыВРознице или ТоварыВНТТ.
Прямо передать имя таблицы или регистра в качестве параметра запроса невозможно, так как параметры предназначены только для передачи значений (ссылок, дат, чисел и строк в условиях). Рассмотрим доступные и эффективные способы решения этой задачи.
Наиболее простое и распространенное решение — написать текст запроса с подстановочной маркой (меткой-заполнителем), после чего перед выполнением запроса заменить её на фактическое имя регистра с помощью функции встроенного языка СтрЗаменить.
Разберем по шагам, как правильно реализовать данный подход:
НужныйРегистр.СтрЗаменить к свойству Запрос.Текст.Посмотрим на пример кода:
// Определяем имя регистра
Если Ссылка.СкладОтправитель.ВидСклада = Перечисления.ВидыСкладов.НТТ Тогда
ИмяРегистра = "ТоварыВНТТ";
ИначеЕсли Ссылка.СкладОтправитель.ВидСклада = Перечисления.ВидыСкладов.Розничный Тогда
ИмяРегистра = "ТоварыВРознице";
Иначе
ИмяРегистра = "ТоварыНаСкладах";
КонецЕсли;
// Формируем текст запроса с меткой
ТекстЗапроса = "ВЫБРАТЬ
| РеализацияТоваровУслугТовары.НомерСтроки КАК НомерСтроки,
| РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
| РеализацияТоваровУслугТовары.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
| РеализацияТоваровУслугТовары.СерияНоменклатуры КАК СерияНоменклатуры,
| СУММА(РеализацияТоваровУслугТовары.Количество) КАК КоличествоНужно,
| СУММА(ЕСТЬNULL(ТоварыОстатки.КоличествоОстаток, 0)) КАК КоличествоОстаток
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.НужныйРегистр.Остатки(&ВремяДока, Склад = &Склад) КАК ТоварыОстатки
| ПО РеализацияТоваровУслугТовары.Номенклатура = ТоварыОстатки.Номенклатура
| И РеализацияТоваровУслугТовары.ХарактеристикаНоменклатуры = ТоварыОстатки.ХарактеристикаНоменклатуры
| И РеализацияТоваровУслугТовары.СерияНоменклатуры = ТоварыОстатки.СерияНоменклатуры
|ГДЕ
| РеализацияТоваровУслугТовары.Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО
| РеализацияТоваровУслугТовары.НомерСтроки,
| РеализацияТоваровУслугТовары.Номенклатура,
| РеализацияТоваровУслугТовары.ХарактеристикаНоменклатуры,
| РеализацияТоваровУслугТовары.СерияНоменклатуры";
// Подставляем реальное имя регистра
Запрос = Новый Запрос;
Запрос.Текст = СтрЗаменить(ТекстЗапроса, "НужныйРегистр", ИмяРегистра);
Важное преимущество данного подхода: при такой структуре исходного текста запроса сохраняется возможность открывать и редактировать текст с помощью встроенного Конструктора запроса. Если же склеивать текст запроса из нескольких кусочков строк с помощью знака сложения +, Конструктор запроса работать перестанет.
В современных версиях платформы 1С:Предприятие существует программный объект СхемаЗапроса, позволяющий изменять структуру запросов объектным способом без прямых манипуляций со строками.
Проанализируем этот вариант: мы можем загрузить исходный текст запроса в объект СхемаЗапроса, выбрать нужный оператор и программно заменить имя источника данных на требуемое. Это защищает от ошибок опечаток и синтаксиса при подстановке строк.
Если требуется сохранить чистый запрос без программной замены текста, можно написать запрос, выбирающий остатки из всех возможных регистров и объединяющий их через ОБЪЕДИНИТЬ ВСЕ, где для каждого блока задать условие по параметру вида склада.
Однако такой вариант может усложнить читаемость запроса и снизить производительность, поэтому подстановка через СтрЗаменить остаётся наиболее оптимальным балансом между удобством разработки и скоростью выполнения.
Проанализируем исходный запрос и выделим несколько критических моментов, которые влияют на производительность и корректность получаемых данных:
Склад = &Склад в параметрах виртуальной таблицы остатков обязательно. Однако если передавать только склад, система прочитает остатки по всем позициям данного склада. Чтобы ускорить выборку, передавайте в параметры виртуальной таблицы список номенклатуры документа с помощью отбора: Номенклатура В (&МассивНоменклатуры) или через временную таблицу.НомерСтроки при левом соединении с остатками может привести к задвоению данных или некорректному списанию, если одинаковая номенклатура встречается в нескольких строках документа. Правильнее сначала поместить свернутую табличную часть документа во временную таблицу, а затем соединять её с виртуальной таблицей остатков.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент и Ссылка.СкладОтправитель создают скрытые левые соединения к реквизитам на уровне СУБД. Значения реквизитов шапки рекомендуется передавать отдельными параметрами запроса.&ВремяДока при проведении документа необходимо передавать объект МоментВремени или Граница, чтобы остатки рассчитывались корректно с учетом позиции документа на оси времени.