При организации интеграции между базами 1С:Предприятия 7.7 через COM/OLE разработчики нередко сталкиваются с ситуацией, когда метод Выполнить() объекта Запрос возвращает значение 0 с системным сообщением «Неизвестная ошибка». При этом тот же самый запрос внутри целевой базы отрабатывает без сбоев. Разберем по шагам причины такого поведения и выясним, как правильно строить запросы и передавать параметры во внешний контекст.
В языке запросов 1С 7.7 строковое представление даты внутри условия должно быть строго заключено в одинарные кавычки (апострофы). Если в блоке Период апострофы часто расставляются автоматически, то в секции Условие конкатенация переменных без апострофов приводит к синтаксической ошибке парсера запросов.
Проанализируем фрагмент ошибочного кода:
// Ошибка: даты подставляются как числа или неэкранированные значения
ТекстЗапроса = ТекстЗапроса + "Условие ((НаДату>="+ВНП+") и (НаДату<="+ВКП+"));";
Для корректного разбора парсером запроса значения дат необходимо явно оборачивать в апострофы:
// Корректный вариант с обрамлением апострофами
ТекстЗапроса = ТекстЗапроса + "Условие ((НаДату>='" + ВНП + "') и (НаДату<='" + ВКП + "'));";
Когда мы передаем в текст запроса строку вида Условие(СкладД = ВыбСклад);, парсер OLE-сервера ищет переменную с именем ВыбСклад в своем собственном контексте. Локальная переменная вызывающей конфигурации на стороне OLE-базы не существует. В результате запрос падает с ошибкой разбора выражения.
Разберем основные варианты решения этой проблемы:
Код) и подставляем его в условие запроса, обрамляя кавычками.Перем глПарам[10] Экспорт;). В вызывающей базе элементу массива присваивается ссылка на OLE-объект, после чего в тексте запроса пишется Условие(СкладД = глПарам[1]);.EvalExpr:Посмотрим на практическую реализацию наиболее простого и надежного первого варианта через код элемента:
// В тексте запроса обращаемся к коду реквизита
ТекстЗапроса =
"Период с '" + ВНП + "' по '" + ВКП + "';
|Док = Документ.ЗаказТовара.ТекущийДокумент;
|СкладКод = Документ.ЗаказТовара.Склад.Код;
|Группировка Док;
|Функция КолВсего = Сумма(Количество);
|Условие ((НаДату >= '" + ВНП + "') и (НаДату <= '" + ВКП + "'));";
Если ПустоеЗначение(ВыбСклад) = 0 Тогда
// Получаем код склада и экранируем его двойными кавычками внутри запроса
КодВыбСклада = СокрЛП(ВыбСклад.Код);
ТекстЗапроса = ТекстЗапроса + "Условие(СкладКод = """ + КодВыбСклада + """);";
КонецЕсли;
Еще одна типичная ошибка возникает на этапе получения результата выборки. Если создать Таблицу Значений в локальной базе вызовом СоздатьОбъект("ТаблицаЗначений"), а затем передать ее в метод удаленного запроса Запрос.Выгрузить(ТЗ1), вызов завершится аварийно.
Объект Запрос, запущенный через OLE, может выгружать данные только в объект ТаблицаЗначений, созданный в том же OLE-соединении.
Разберем правильный порядок действий:
// 1. Создаем Таблицу Значений в контексте OLE-сервера
ТЗ_Оле = Оле.CreateObject("ТаблицаЗначений");
// 2. Выгружаем результат выполненного запроса
Запрос.Выгрузить(ТЗ_Оле);
// 3. Создаем локальную Таблицу Значений для работы в текущей базе
ТЗ_Локальная = СоздатьОбъект("ТаблицаЗначений");
ТЗ_Локальная.НоваяКолонка("Документ");
ТЗ_Локальная.НоваяКолонка("Количество", "Число", 15, 2);
// 4. Построчно переносим данные из OLE-таблицы в локальную
ТЗ_Оле.ВыбратьСтроки();
Пока ТЗ_Оле.ПолучитьСтроку() = 1 Цикл
ТЗ_Локальная.НоваяСтрока();
// Извлекаем примитивные типы данных во избежание накопления COM-ссылок
ТЗ_Локальная.Документ = СокрЛП(ТЗ_Оле.Док);
ТЗ_Локальная.Количество = ТЗ_Оле.КолВсего;
КонецЦикла;
Объединим рассмотренные правила в единый законченный блок кода:
Оле = СоздатьОбъект("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)");
Оле = "";
Код, НомерДок, ДатаДок), а не ссылочные объекты. Это снижает нагрузку на память и предотвращает блокировку интерфейса модальными окнами COM («Сервер занят»).Оле = "" вызывайте команду Оле.ExecuteBatch("ЗавершитьРаботуСистемы(0)").