Как вернуть строковое значение или константу в запросе 1С 7.7?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

Разработчики, имеющие опыт работы с платформой 1С:Предприятие 8.х, привыкли к гибкости локального языка запросов. В "восьмерке" мы можем легко написать конструкцию вида ВЫБРАТЬ "Розничный покупатель" КАК Покупатель и получить готовую колонку со строковой константой прямо из СУБД. Однако при работе с унаследованными системами на платформе 1С:Предприятие 7.7 мы сталкиваемся с жесткими ограничениями встроенного языка запросов.

Попытка напрямую присвоить переменной запроса строковый литерал или внешнюю переменную (например, написать Покупатель = "Розничный покупатель"; в тексте запроса) приведет к ошибке компиляции. Парсер запросов 1С 7.7 ожидает увидеть в правой части выражения только физический путь к данным метаданных (реквизитам справочников, документов или регистров). В этой статье мы подробно разберем, как решить эту задачу профессионально, используя стандартные механизмы работы с Таблицей Значений, возможности прямых SQL-запросов и правильную оптимизацию структуры самого запроса.

Метод 1. Использование Таблицы Значений (Рекомендуемый стандартный способ)

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

Проанализируем этот алгоритм по шагам:

  1. Создаем и выполняем запрос без лишних группировок.
  2. Выгружаем результат запроса в объект ТаблицаЗначений в виде плоской таблицы (без иерархии).
  3. Добавляем новую колонку с типом "Строка" или типом конкретного справочника.
  4. Используем метод Заполнить() для моментальной вставки нужного значения во все строки таблицы.

Рассмотрим подробнее практический пример реализации этого метода:


// 1. Описываем и выполняем стандартный запрос
ТекстЗапроса = "
|//{{ЗАПРОС(Продажа)
|Период с (НачГода(ТекущаяДата()));
|Без Итогов;
|Обрабатывать НеПомеченныеНаУдаление;
|Номенклатура = Регистр.Продажи.Номенклатура;
|Количество = Регистр.Продажи.Количество;
|Функция КоличествоСумма = Сумма(Количество);
|Группировка День;
|Группировка Номенклатура без групп;
|//}}ЗАПРОС
|";

Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Предупреждение("Ошибка выполнения запроса!");
    Возврат;
КонецЕсли;

// 2. Выгружаем результат в Таблицу Значений. 
// Критически важно передать параметры (ТЗ, 0, 0), чтобы получить плоскую структуру данных без древовидных итогов
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ, 0, 0);

// 3. Добавляем новую колонку для покупателя
ТЗ.НоваяКолонка("Покупатель", "Строка", 50);

// 4. Заполняем всю колонку фиксированным текстом
ТЗ.Заполнить("Розничный покупатель", , , "Покупатель");

// Теперь ТЗ готова для вывода в отчет или дальнейшей обработки
ТЗ.ВыбратьСтроку();

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


// Предварительно находим элемент справочника в базе данных
СпрКонтрагенты = СоздатьОбъект("Справочник.Контрагенты");
Если СпрКонтрагенты.НайтиПоНаименованию("Розничный покупатель", 0, 1) = 1 Тогда
    СсылкаНаПокупателя = СпрКонтрагенты.ТекущийЭлемент();
Иначе
    СсылкаНаПокупателя = ПолучитьПустоеЗначение("Справочник.Контрагенты");
КонецЕсли;

// Выгружаем запрос в ТЗ
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ, 0, 0);

// Добавляем типизированную колонку справочника
ТЗ.НоваяКолонка("Покупатель", "Справочник.Контрагенты");

// Заполняем колонку найденной ссылкой на элемент
ТЗ.Заполнить(СсылкаНаПокупателя, , , "Покупатель");

Данный подход является наиболее надежным, безопасным и простым в отладке. Он не требует сторонних библиотек и стабильно работает в любых конфигурациях 1С 7.7.

Метод 2. Прямые SQL-запросы через внешнюю компоненту 1С++

Если ваша база данных 1С 7.7 работает под управлением Microsoft SQL Server, либо вы используете локальные таблицы DBF с установленной внешней компонентой 1С++, вы можете полностью обойти любые ограничения стандартного интерпретатора запросов. Класс ODBCRecordset позволяет выполнять прямые SQL-инструкции к таблицам базы данных.

В прямом SQL-запросе мы можем использовать стандартный синтаксис SQL, включая строковые литералы, явное приведение типов и создание колонок "на лету". Выясним, как это выглядит на практике:


// Создаем объект для работы с прямым SQL-запросом
ЗапросSQL = СоздатьОбъект("ODBCRecordset");

// Формируем текст SQL-запроса, где жестко задаем строковую константу
ТекстЗапроса = "
|SELECT 
|   $Рег.Номенклатура AS [Номенклатура $Справочник.Номенклатура],
|   $Рег.Количество AS Количество,
|   'Розничный покупатель' AS Покупатель
|FROM 
|   $Регистр.Продажи AS Рег
|";

// Выполняем инструкцию и сразу получаем готовую Таблицу Значений
ТЗ = ЗапросSQL.ВыполнитьИнструкцию(ТекстЗапроса);

Преимущества данного метода:

Метод 3. Использование пользовательских функций в агрегатных вычислениях (Скрытый хак)

Существует малоизвестная и недокументированная возможность встроенного языка запросов 1С 7.7 — вызов функций конфигурации внутри агрегатных вычислений тела запроса. Мы не можем написать |Покупатель = МояФункция() напрямую, однако мы можем обернуть вызов в агрегатную функцию Сумма.

Разберем этот специфический метод подробнее. Для его реализации в Глобальном модуле (или в модуле самого отчета с ключевым словом Экспорт) необходимо описать функцию, возвращающую нужное текстовое значение:


Функция ПолучитьКонстантуПокупателя() Экспорт
    Возврат "Розничный покупатель";
КонецФункции

В тексте стандартного запроса мы можем прописать вычисление функции следующим образом:

|Функция Покупатель = Сумма(ПолучитьКонстантуПокупателя());

Важное предупреждение: этот метод считается крайне нежелательным ("костыльным") среди профессиональных разработчиков по следующим причинам:

  1. Резкое падение производительности. Поскольку функция вызывается для каждой записи таблицы базы данных на этапе сканирования, выполнение запроса на большой базе данных может занять чрезвычайно много времени или вызвать зависание программы.
  2. Нечитаемость кода. Использование математической функции Сумма() для работы со строковыми данными усложняет чтение и поддержку кода другими разработчиками.

Оптимизация структуры запроса и исправление избыточных группировок

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


|Группировка День;
|Группировка Покупатель;
|Группировка Номенклатура без групп;
|Группировка Артикул без групп;
|Группировка ЮрФизЛицо без групп;

Такой набор группировок является избыточным и непроизводительным. Каждая новая группировка создает дополнительный уровень вложенности в иерархическом дереве результатов запроса. Это сильно замедляет работу СУБД и усложняет алгоритм обхода результата в коде.

Выясним причину нерациональности:

Как правильно оптимизировать структуру?

Оставляйте группировки только по ключевым сущностям (например, День, Покупатель, Номенклатура). Любые подчиненные реквизиты этих объектов (такие как артикул номенклатуры или юридический статус покупателя) необходимо получать непосредственно в цикле обхода результатов запроса через точку от основного объекта группировки.

Рассмотрим правильный пример обхода оптимизированного запроса:


// В запросе оставляем только ключевые группировки
|Группировка День;
|Группировка Номенклатура без групп;

// При обходе результата получаем подчиненные реквизиты напрямую
Пока Запрос.Группировка("День") = 1 Цикл
    Пока Запрос.Группировка("Номенклатура") = 1 Цикл
        // Получаем артикул напрямую без создания отдельной группировки в запросе!
        ТекущийАртикул = Запрос.Номенклатура.Артикул; 
        
        // Выводим данные в печатную форму или ТЗ
        Таб.ВывестиСекцию("Строка");
    КонецЦикла;
КонецЦикла;

Использование этого подхода позволяет существенно сократить время выполнения запроса сервером баз данных и делает код чистым, лаконичным и профессиональным.

← На главную