Как правильно объединить запрос сам с собой в 1С и исправить синтаксическую ошибку "ОБЪЕДИНИТЬ"?

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

При динамическом формировании отчетов в 1С:Предприятие 8 программисты часто сталкиваются с необходимостью объединения нескольких запросов. Одной из типичных задач является дублирование данных или сборка сложного отчета (например, "шахматки") путем программной склейки строк. Однако простая конкатенация строк вида ТекстЗапроса = ТекстЗапроса + "ОБЪЕДИНИТЬ ВСЕ" + ТекстЗапроса практически всегда приводит к ошибке: {(..., ...)}: Синтаксическая ошибка "ОБЪЕДИНИТЬ". В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем правила использования операторов объединения и рассмотрим правильные способы программного построения текста запроса.

Разбор причины ошибки: почему "склеивание" не работает?

Рассмотрим ситуацию, с которой столкнулся автор темы. В коде была процедура УстановитьТекстЗапроса(), которая формировала текст для объекта ПостроительОтчета. После того как переменная ТекстЗапроса была заполнена, программист попытался добавить к ней её же копию через ОБЪЕДИНИТЬ ВСЕ. Ошибка возникла из-за нарушения структуры языка запросов 1С.

Главное правило: такие секции, как УПОРЯДОЧИТЬ ПО и ИТОГИ, являются глобальными для всего запроса в целом. Они не могут находиться внутри объединяемых частей. Если ваш исходный запрос содержал итоги, то после объединения структура превратилась в нечто подобное:


ВЫБРАТЬ Поля ИЗ Таблица
ИТОГИ СУММА(Поле) ПО ОБЩИЕ
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ Поля ИЗ Таблица
ИТОГИ СУММА(Поле) ПО ОБЩИЕ

Платформа 1С ожидает увидеть оператор ОБЪЕДИНИТЬ сразу после секции ГДЕ, СГРУППИРОВАТЬ ПО или ИМЕЮЩИЕ. Встретив ключевое слово ИТОГИ в середине текста, парсер запроса считает основной запрос завершенным, и последующий оператор ОБЪЕДИНИТЬ вызывает фатальную ошибку синтаксиса.

Правила использования ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ

Прежде чем исправлять код, проанализируем основные требования платформы к объединениям:

  1. Количество и порядок полей: Все части объединения должны содержать одинаковое количество колонок. Типы данных в соответствующих колонках должны быть совместимыми.
  2. Псевдонимы: Имена полей (псевдонимы) для результирующей выборки всегда берутся из первого запроса. Во втором и последующих запросах псевдонимы игнорируются, но их наличие не является ошибкой.
  3. Глобальные секции: Операторы УПОРЯДОЧИТЬ ПО, ИТОГИ и АВТОУПОРЯДОЧИВАНИЕ могут быть указаны только один раз в самом конце всего составного запроса.

Решение №1: Правильная сборка текста запроса по шагам

Чтобы корректно объединить запрос сам с собой, необходимо разделить формирование "тела" выборки и формирование "хвоста" (итогов и сортировки). Рассмотрим подробнее, как это реализовать программно:


// 1. Формируем только основную часть выборки (БЕЗ итогов и сортировки)
ТекстВыборки = 
"ВЫБРАТЬ
|   Взаиморасчеты.Контрагент,
|   Взаиморасчеты.СуммаОстаток КАК Долг
|ИЗ
|   РегистрНакопления.Взаиморасчеты.Остатки(&Дата, ) КАК Взаиморасчеты";

// 2. Объединяем части
ТекстЗапроса = ТекстВыборки + "
| ОБЪЕДИНИТЬ ВСЕ
| " + ТекстВыборки;

// 3. И только теперь добавляем итоги ко всему результату
ТекстЗапроса = ТекстЗапроса + "
|ИТОГИ
|   СУММА(Долг)
|ПО
|   ОБЩИЕ";

Такой подход гарантирует, что структура запроса останется легитимной, и ПостроительОтчета (или Запрос) успешно его скомпилирует.

Решение №2: Использование временных таблиц

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


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

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

Запрос.Выполнить();

// Теперь объединяем данные из ВТ столько раз, сколько нужно
Запрос.Текст = 
"ВЫБРАТЬ Контрагент, Долг ИЗ ВТ_Данные
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ Контрагент, Долг ИЗ ВТ_Данные
|ИТОГИ СУММА(Долг) ПО ОБЩИЕ";

Использование МенеджерВременныхТаблиц особенно полезно в Универсальном отчете, так как позволяет избежать многократного обращения к виртуальным таблицам регистров (остаткам и оборотам), что существенно повышает производительность.

Особенности работы с "Универсальным отчетом" и Построителем

В тексте темы автор использует УниверсальныйОтчет. Этот механизм автоматически модифицирует текст запроса, добавляя туда поля свойств и категорий через методы ДобавитьВТекстЗапросаСвойстваИКатегории().

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

Разберем, как правильно интегрировать объединение в структуру УниверсальногоОтчета:

  1. Сформируйте переменную с текстом подзапроса (тела).
  2. Соберите итоговый текст, используя ОБЪЕДИНИТЬ ВСЕ.
  3. Убедитесь, что управляющие конструкции ({ИТОГИ}, ИТОГИ ПО) добавлены только в финальную строку.
  4. Вызывайте УниверсальныйОтчет.ДобавитьВТекстЗапросаСвойстваИКатегории(ТекстЗапроса) только после полной сборки структуры.

Когда стоит отказаться от объединения в пользу СКД?

Если ваша конечная цель — создание отчета типа "шахматка" (cross-tab), где данные должны дублироваться или распределяться по разным колонкам в зависимости от условий, программная склейка текста запроса является трудоемким и низкоэффективным методом. Специалисты рекомендуют использовать Систему компоновки данных (СКД).

В СКД задача решается гораздо проще:

Система сама сформирует необходимый SQL-запрос, выполнит все соединения и агрегацию данных. Это избавит вас от необходимости бороться с синтаксическими ошибками ОБЪЕДИНИТЬ и ручного управления итогами.

Таким образом, мы выяснили, что ошибка автора была вызвана нарушением целостности структуры запроса. Для решения подобных задач всегда следуйте принципу отделения логики выборки данных от логики их оформления (сортировки и итогов). Помните, что ИТОГИ — это финальный аккорд вашего запроса, и звучать он должен только один раз в самом конце.

← На главную