Почему не выполняется запрос по OLE в 1С 7.7 и как исправить «Неизвестную ошибку»?

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

При организации интеграции между базами 1С:Предприятия 7.7 через COM/OLE разработчики нередко сталкиваются с ситуацией, когда метод Выполнить() объекта Запрос возвращает значение 0 с системным сообщением «Неизвестная ошибка». При этом тот же самый запрос внутри целевой базы отрабатывает без сбоев. Разберем по шагам причины такого поведения и выясним, как правильно строить запросы и передавать параметры во внешний контекст.

Причина 1: Некорректный синтаксис литералов дат в условии запроса

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

Проанализируем фрагмент ошибочного кода:


// Ошибка: даты подставляются как числа или неэкранированные значения
ТекстЗапроса = ТекстЗапроса + "Условие ((НаДату>="+ВНП+") и (НаДату<="+ВКП+"));";

Для корректного разбора парсером запроса значения дат необходимо явно оборачивать в апострофы:


// Корректный вариант с обрамлением апострофами
ТекстЗапроса = ТекстЗапроса + "Условие ((НаДату>='" + ВНП + "') и (НаДату<='" + ВКП + "'));";

Причина 2: Попытка использовать локальные переменные в OLE-контексте

Когда мы передаем в текст запроса строку вида Условие(СкладД = ВыбСклад);, парсер OLE-сервера ищет переменную с именем ВыбСклад в своем собственном контексте. Локальная переменная вызывающей конфигурации на стороне OLE-базы не существует. В результате запрос падает с ошибкой разбора выражения.

Разберем основные варианты решения этой проблемы:

  1. Передача примитивного значения (кода или наименования) прямо в текст запроса:
    Вместо передачи ссылки на объект получаем строковый реквизит (например, Код) и подставляем его в условие запроса, обрамляя кавычками.
  2. Использование глобальных переменных удаленной базы:
    В глобальном модуле OLE-базы объявляется экспортируемый массив (например, Перем глПарам[10] Экспорт;). В вызывающей базе элементу массива присваивается ссылка на OLE-объект, после чего в тексте запроса пишется Условие(СкладД = глПарам[1]);.
  3. Инициализация переменных через метод EvalExpr:
    Создание и наполнение нужных переменных непосредственно во внешней среде исполнения без изменения глобального модуля целевой конфигурации.

Посмотрим на практическую реализацию наиболее простого и надежного первого варианта через код элемента:


// В тексте запроса обращаемся к коду реквизита
ТекстЗапроса = 
"Период с '" + ВНП + "' по '" + ВКП + "';
|Док = Документ.ЗаказТовара.ТекущийДокумент;
|СкладКод = Документ.ЗаказТовара.Склад.Код;
|Группировка Док;
|Функция КолВсего = Сумма(Количество);
|Условие ((НаДату >= '" + ВНП + "') и (НаДату <= '" + ВКП + "'));";

Если ПустоеЗначение(ВыбСклад) = 0 Тогда
    // Получаем код склада и экранируем его двойными кавычками внутри запроса
    КодВыбСклада = СокрЛП(ВыбСклад.Код);
    ТекстЗапроса = ТекстЗапроса + "Условие(СкладКод = """ + КодВыбСклада + """);";
КонецЕсли;

Причина 3: Несовместимость контекстов Таблицы Значений при выгрузке

Еще одна типичная ошибка возникает на этапе получения результата выборки. Если создать Таблицу Значений в локальной базе вызовом СоздатьОбъект("ТаблицаЗначений"), а затем передать ее в метод удаленного запроса Запрос.Выгрузить(ТЗ1), вызов завершится аварийно.

Объект Запрос, запущенный через OLE, может выгружать данные только в объект ТаблицаЗначений, созданный в том же OLE-соединении.

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


// 1. Создаем Таблицу Значений в контексте OLE-сервера
ТЗ_Оле = Оле.CreateObject("ТаблицаЗначений");

// 2. Выгружаем результат выполненного запроса
Запрос.Выгрузить(ТЗ_Оле);

// 3. Создаем локальную Таблицу Значений для работы в текущей базе
ТЗ_Локальная = СоздатьОбъект("ТаблицаЗначений");
ТЗ_Локальная.НоваяКолонка("Документ");
ТЗ_Локальная.НоваяКолонка("Количество", "Число", 15, 2);

// 4. Построчно переносим данные из OLE-таблицы в локальную
ТЗ_Оле.ВыбратьСтроки();
Пока ТЗ_Оле.ПолучитьСтроку() = 1 Цикл
    ТЗ_Локальная.НоваяСтрока();
    // Извлекаем примитивные типы данных во избежание накопления COM-ссылок
    ТЗ_Локальная.Документ = СокрЛП(ТЗ_Оле.Док);
    ТЗ_Локальная.Количество = ТЗ_Оле.КолВсего;
КонецЦикла;

Сводный пример корректного выполнения запроса через OLE

Объединим рассмотренные правила в единый законченный блок кода:


Оле = СоздатьОбъект("V77.Application");
СтрокаПодключения = "/d" + СокрЛП(ПутьКБазе) + " /n" + СокрЛП(Пользователь) + " /p" + Пароль;

Если Оле.Initialize(Оле.RMTrade, СтрокаПодключения, "NO_SPLASH_SHOW") = 0 Тогда
    Предупреждение("Не удалось подключиться к информационной базе по OLE!");
    Возврат;
КонецЕсли;

ВНП = Дата1 - 7;
ВКП = Дата2 + 3;

// Формируем текст запроса с правильным экранированием дат
ТекстЗапроса = 
"Период с '" + ВНП + "' по '" + ВКП + "';
|Док = Документ.ЗаказТовара.ТекущийДокумент;
|ТоварКод = Документ.ЗаказТовара.Товар.Код;
|СкладКод = Документ.ЗаказТовара.Склад.Код;
|Количество = Документ.ЗаказТовара.Количество;
|НаДату = Документ.ЗаказТовара.НаДату;
|Группировка Док;
|Группировка ТоварКод Без Групп;
|Функция КолВсего = Сумма(Количество);
|Условие ((НаДату >= '" + ВНП + "') и (НаДату <= '" + ВКП + "'));";

// Добавляем условие по коду выбранного склада
Если ПустоеЗначение(КодСкладаПоиска) = 0 Тогда
    ТекстЗапроса = ТекстЗапроса + "Условие(СкладКод = """ + СокрЛП(КодСкладаПоиска) + """);";
КонецЕсли;

Запрос = Оле.CreateObject("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Сообщить("Ошибка выполнения запроса в OLE-базе!", "!");
    Оле.ExecuteBatch("ЗавершитьРаботуСистемы(0)");
    Оле = "";
    Возврат;
КонецЕсли;

// Выгружаем данные во временную OLE-таблицу
ТЗ_Оле = Оле.CreateObject("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ_Оле);

// Корректно закрываем сессию OLE после получения данных
Оле.ExecuteBatch("ЗавершитьРаботуСистемы(0)");
Оле = "";

Рекомендации по стабильной работе с OLE в 1С 7.7

  1. Передавайте только примитивные типы: Старайтесь получать из запроса числа, строки и даты (например, Код, НомерДок, ДатаДок), а не ссылочные объекты. Это снижает нагрузку на память и предотвращает блокировку интерфейса модальными окнами COM («Сервер занят»).
  2. Гарантированно освобождайте сеанс: Чтобы удаленная база не оставалась заблокированной в диспетчере процессов, перед присвоением Оле = "" вызывайте команду Оле.ExecuteBatch("ЗавершитьРаботуСистемы(0)").
← На главную