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

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

При интеграции баз данных на платформе 1С:Предприятие 7.7 через механизм OLE Automation разработчики часто сталкиваются с ошибками при попытке выполнить запрос с условиями по переменным. Типичная ошибка выглядит следующим образом: Ошибка в выражении 'ТекСотрудникОЛЕ' или Переменная не объявлена как массив.

Разберем подробно причины возникновения этой ошибки и выясним, как правильно строить запросы к данным удаленной информационной базы.

Почему возникает ошибка при выполнении OLE-запроса

Проанализируем контекст выполнения кода. Когда мы создаем объект Запрос через OLE-соединение:

ЗапросОЛЕ = ОЛЕ.CreateObject("Запрос");

Этот объект создается и компилируется строго во внутреннем контексте сервера OLE-базы. Локальные переменные базы-клиента (инициатора подключения) физически недоступны для парсера запроса внутри удаленной базы. Если написать в тексте запроса конструкцию вида |Условие(Объект = ТекСотрудникОЛЕ);, синтаксический анализатор удаленной базы не найдет переменную ТекСотрудникОЛЕ в своем локальном пространстве имен и выдаст ошибку.

Кроме того, язык запросов 1С 7.7 накладывает жесткие синтаксические ограничения:

  1. В секции Условие(...) нельзя использовать обращение к элементам массивов по индексу (например, СписокВР[1] вызовет синтаксическую ошибку).
  2. Метод ExecuteBatch выполняет только исполняемые операторы, объявить через него новую переменную с директивой Перем в рантайме невозможно, так как переменные компилируются при инициализации модуля.
  3. В условиях запроса парсер ожидает либо вызовы доступных глобальных функций, либо имена глобальных экспортных переменных, либо конкретные константы.

Способ 1. Использование экспортных глобальных переменных OLE-базы

Рассмотрим основной способ передачи параметров в OLE-запрос. Чтобы удаленный объект Запрос увидел значение фильтра, его необходимо поместить в глобальную экспортную переменную удаленной базы.

В большинстве типовых конфигураций 1С 7.7 (включая конфигурации по расчету зарплаты и учету) в Глобальном модуле уже присутствуют предопределенные служебные экспортные переменные, такие как глРасшифровка или глФильтр. Если конфигурация позволяет внесение изменений, можно объявить собственные экспортные переменные:


// В Глобальном модуле базы-источника (OLE-базы):
Перем глСотрудникДляЗапроса Экспорт;
Перем глСписокВР Экспорт;

Посмотрим на пример кода в базе-клиенте, использующий глобальную переменную:


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

// Создание и наполнение списка значений в контексте OLE-базы
СписокВРОЛЕ = ОЛЕ.CreateObject("СписокЗначений");
СписокВРОЛЕ.ДобавитьЗначение(ОЛЕ.ВидРасчета.ОтпускОчередной);
СписокВРОЛЕ.ДобавитьЗначение(ОЛЕ.ВидРасчета.ОтпускДополнительный);
СписокВРОЛЕ.ДобавитьЗначение(ОЛЕ.ВидРасчета.КомпенсацияОтпускаСверх28дней);
СписокВРОЛЕ.ДобавитьЗначение(ОЛЕ.ВидРасчета.КомпенсацияОтпуска);

// Помещаем список во вторую доступную глобальную переменную или используем свойства
ОЛЕ.глФильтр = СписокВРОЛЕ;

ЗапросОЛЕ = ОЛЕ.CreateObject("Запрос");
ТекстЗапросаОЛЕ = "Период с '" + ДатаНачалаОтпускаОЛЕ + "' по '" + ДатаОкончанияОтпускаОЛЕ + "';
|ТекущийДокумент = ЖурналРасчетов.Зарплата.ТекущийДокумент;
|ДатаНачала = ЖурналРасчетов.Зарплата.ДатаНачала;
|ДатаОкончания = ЖурналРасчетов.Зарплата.ДатаОкончания;
|Объект = ЖурналРасчетов.Зарплата.Объект;
|ВидРасч = ЖурналРасчетов.Зарплата.ВидРасч;
|Дни = ЖурналРасчетов.Зарплата.Дни;
|Результат = ЖурналРасчетов.Зарплата.Результат;
|Функция ДниСумма = Сумма(Дни);
|Функция РезультатВсего = Сумма(Результат);
|Группировка Объект;
|Группировка ВидРасч;
|Группировка ТекущийДокумент;
|Группировка ДатаНачала;
|Группировка ДатаОкончания;
|Условие(Объект = глРасшифровка);
|Условие(ВидРасч в глФильтр);
|";

Если ЗапросОЛЕ.Выполнить(ТекстЗапросаОЛЕ) = 1 Тогда
    Пока ЗапросОЛЕ.Группировка(1) = 1 Цикл
        // Обработка результатов
    КонецЦикла;
КонецЕсли;

Способ 2. Выборка через методы Журнала Расчетов

Разберем альтернативный вариант. Если требуется получить записи только по одному конкретному сотруднику, обращение через объект Запрос по OLE может быть избыточным и медленным. В объекте ЖурналРасчетов платформы 1С 7.7 предусмотрены встроенные методы позиционирования, которые работают значительно быстрее:


ЖР_ОЛЕ = ОЛЕ.CreateObject("ЖурналРасчетов.Зарплата");
СпрСотрудникиОЛЕ = ОЛЕ.CreateObject("Справочник.Сотрудники");

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

Способ 3. Перенос логики формирования отчета во внешнюю обработку

Рассмотрим наиболее производительный подход при больших объемах данных. При выполнении запроса через OLE-соединение каждый вызов методов Группировка() и обращение к реквизитам выборки приводит к отдельному сетевому/межпроцессному вызову (COM Marshaling), что существенно замедляет выполнение кода.

Оптимальным решением является:

  1. Создать внешнюю обработку (.ert), в которой запрос формируется и обрабатывается локально внутри OLE-базы.
  2. Свернуть результат выборки в ТаблицуЗначений.
  3. Сериализовать таблицу в строку с помощью метода ЗначениеВСтрокуВнутр().
  4. Запустить обработку через OLE и вернуть готовую строку в основную базу, восстановив данные методом ЗначениеИзСтрокиВнутр().

Такой подход полностью устраняет проблемы с контекстом переменных в запросе и ускоряет передачу данных в десятки раз.

← На главную