Разработчики, имеющие опыт работы с платформой 1С:Предприятие 8.х, привыкли к гибкости локального языка запросов. В "восьмерке" мы можем легко написать конструкцию вида ВЫБРАТЬ "Розничный покупатель" КАК Покупатель и получить готовую колонку со строковой константой прямо из СУБД. Однако при работе с унаследованными системами на платформе 1С:Предприятие 7.7 мы сталкиваемся с жесткими ограничениями встроенного языка запросов.
Попытка напрямую присвоить переменной запроса строковый литерал или внешнюю переменную (например, написать Покупатель = "Розничный покупатель"; в тексте запроса) приведет к ошибке компиляции. Парсер запросов 1С 7.7 ожидает увидеть в правой части выражения только физический путь к данным метаданных (реквизитам справочников, документов или регистров). В этой статье мы подробно разберем, как решить эту задачу профессионально, используя стандартные механизмы работы с Таблицей Значений, возможности прямых SQL-запросов и правильную оптимизацию структуры самого запроса.
Поскольку сам запрос 1С 7.7 не умеет динамически конструировать текстовые поля, лучшим решением является постобработка результатов запроса с помощью объекта ТаблицаЗначений. Мы можем выгрузить результат запроса в плоскую таблицу, добавить в нее колонку с нужным типом данных и заполнить ее требуемым значением за одну простую операцию.
Проанализируем этот алгоритм по шагам:
ТаблицаЗначений в виде плоской таблицы (без иерархии).Заполнить() для моментальной вставки нужного значения во все строки таблицы.Рассмотрим подробнее практический пример реализации этого метода:
// 1. Описываем и выполняем стандартный запрос
ТекстЗапроса = "
|//{{ЗАПРОС(Продажа)
|Период с (НачГода(ТекущаяДата()));
|Без Итогов;
|Обрабатывать НеПомеченныеНаУдаление;
|Номенклатура = Регистр.Продажи.Номенклатура;
|Количество = Регистр.Продажи.Количество;
|Функция КоличествоСумма = Сумма(Количество);
|Группировка День;
|Группировка Номенклатура без групп;
|//}}ЗАПРОС
|";
Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Предупреждение("Ошибка выполнения запроса!");
Возврат;
КонецЕсли;
// 2. Выгружаем результат в Таблицу Значений.
// Критически важно передать параметры (ТЗ, 0, 0), чтобы получить плоскую структуру данных без древовидных итогов
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ, 0, 0);
// 3. Добавляем новую колонку для покупателя
ТЗ.НоваяКолонка("Покупатель", "Строка", 50);
// 4. Заполняем всю колонку фиксированным текстом
ТЗ.Заполнить("Розничный покупатель", , , "Покупатель");
// Теперь ТЗ готова для вывода в отчет или дальнейшей обработки
ТЗ.ВыбратьСтроку();
Если вам требуется заполнить колонку не просто строковым текстом, а реальной ссылкой на элемент справочника (например, чтобы пользователь мог кликнуть на ячейку в отчете и открыть карточку контрагента), мы можем типизировать новую колонку объектом метаданных. Посмотрим на пример реализации с объектом справочника:
// Предварительно находим элемент справочника в базе данных
СпрКонтрагенты = СоздатьОбъект("Справочник.Контрагенты");
Если СпрКонтрагенты.НайтиПоНаименованию("Розничный покупатель", 0, 1) = 1 Тогда
СсылкаНаПокупателя = СпрКонтрагенты.ТекущийЭлемент();
Иначе
СсылкаНаПокупателя = ПолучитьПустоеЗначение("Справочник.Контрагенты");
КонецЕсли;
// Выгружаем запрос в ТЗ
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ, 0, 0);
// Добавляем типизированную колонку справочника
ТЗ.НоваяКолонка("Покупатель", "Справочник.Контрагенты");
// Заполняем колонку найденной ссылкой на элемент
ТЗ.Заполнить(СсылкаНаПокупателя, , , "Покупатель");
Данный подход является наиболее надежным, безопасным и простым в отладке. Он не требует сторонних библиотек и стабильно работает в любых конфигурациях 1С 7.7.
Если ваша база данных 1С 7.7 работает под управлением Microsoft SQL Server, либо вы используете локальные таблицы DBF с установленной внешней компонентой 1С++, вы можете полностью обойти любые ограничения стандартного интерпретатора запросов. Класс ODBCRecordset позволяет выполнять прямые SQL-инструкции к таблицам базы данных.
В прямом SQL-запросе мы можем использовать стандартный синтаксис SQL, включая строковые литералы, явное приведение типов и создание колонок "на лету". Выясним, как это выглядит на практике:
// Создаем объект для работы с прямым SQL-запросом
ЗапросSQL = СоздатьОбъект("ODBCRecordset");
// Формируем текст SQL-запроса, где жестко задаем строковую константу
ТекстЗапроса = "
|SELECT
| $Рег.Номенклатура AS [Номенклатура $Справочник.Номенклатура],
| $Рег.Количество AS Количество,
| 'Розничный покупатель' AS Покупатель
|FROM
| $Регистр.Продажи AS Рег
|";
// Выполняем инструкцию и сразу получаем готовую Таблицу Значений
ТЗ = ЗапросSQL.ВыполнитьИнструкцию(ТекстЗапроса);
Преимущества данного метода:
CASE WHEN, объединения таблиц (JOIN) и вложенные подзапросы.Существует малоизвестная и недокументированная возможность встроенного языка запросов 1С 7.7 — вызов функций конфигурации внутри агрегатных вычислений тела запроса. Мы не можем написать |Покупатель = МояФункция() напрямую, однако мы можем обернуть вызов в агрегатную функцию Сумма.
Разберем этот специфический метод подробнее. Для его реализации в Глобальном модуле (или в модуле самого отчета с ключевым словом Экспорт) необходимо описать функцию, возвращающую нужное текстовое значение:
Функция ПолучитьКонстантуПокупателя() Экспорт
Возврат "Розничный покупатель";
КонецФункции
В тексте стандартного запроса мы можем прописать вычисление функции следующим образом:
|Функция Покупатель = Сумма(ПолучитьКонстантуПокупателя());
Важное предупреждение: этот метод считается крайне нежелательным ("костыльным") среди профессиональных разработчиков по следующим причинам:
Сумма() для работы со строковыми данными усложняет чтение и поддержку кода другими разработчиками.В процессе обсуждения данной проблемы на форумах часто выявляется сопутствующая ошибка начинающих разработчиков — избыточные и нерациональные группировки запроса. Проанализируем типичный пример некорректного кода группировок:
|Группировка День;
|Группировка Покупатель;
|Группировка Номенклатура без групп;
|Группировка Артикул без групп;
|Группировка ЮрФизЛицо без групп;
Такой набор группировок является избыточным и непроизводительным. Каждая новая группировка создает дополнительный уровень вложенности в иерархическом дереве результатов запроса. Это сильно замедляет работу СУБД и усложняет алгоритм обхода результата в коде.
Выясним причину нерациональности:
Как правильно оптимизировать структуру?
Оставляйте группировки только по ключевым сущностям (например, День, Покупатель, Номенклатура). Любые подчиненные реквизиты этих объектов (такие как артикул номенклатуры или юридический статус покупателя) необходимо получать непосредственно в цикле обхода результатов запроса через точку от основного объекта группировки.
Рассмотрим правильный пример обхода оптимизированного запроса:
// В запросе оставляем только ключевые группировки
|Группировка День;
|Группировка Номенклатура без групп;
// При обходе результата получаем подчиненные реквизиты напрямую
Пока Запрос.Группировка("День") = 1 Цикл
Пока Запрос.Группировка("Номенклатура") = 1 Цикл
// Получаем артикул напрямую без создания отдельной группировки в запросе!
ТекущийАртикул = Запрос.Номенклатура.Артикул;
// Выводим данные в печатную форму или ТЗ
Таб.ВывестиСекцию("Строка");
КонецЦикла;
КонецЦикла;
Использование этого подхода позволяет существенно сократить время выполнения запроса сервером баз данных и делает код чистым, лаконичным и профессиональным.