При организации обмена данными между устаревшими конфигурациями на платформе 1С:Предприятие 7.7 и современными базами на платформе 1С:Предприятие 8.x программисты часто используют прямое COM-соединение (OLE). Одним из наиболее эффективных способов получения данных является выполнение запроса на стороне 1С 8 с последующей обработкой результатов в 1С 7.7.
Однако на этом пути разработчики часто сталкиваются со странным поведением системы: запрос отрабатывает без ошибок, данные в базе-источнике точно есть, но при попытке обойти выборку цикл в 1С 7.7 просто игнорируется. В этой статье мы подробно разберем, почему это происходит, как правильно обходить выборку запроса, а также рассмотрим особенности работы с типами данных при OLE-соединении.
Рассмотрим классический пример кода в 1С 7.7, который вызывает проблему у большинства разработчиков:
ВыборкаБД = ЗапросБД.Выполнить().Выбрать();
Пока ВыборкаБД.Следующий() = 1 Цикл
// Код обработки данных
Сообщить(ВыборкаБД.Номер);
КонецЦикла;
При выполнении этого кода программа просто пропускает цикл, даже если метод ВыборкаБД.Количество() возвращает положительное число документов. Почему так происходит?
Дело в различиях типов данных между платформами 1С 7.7 и 1С 8:
Следующий() возвращает системный тип Булево (значения Истина или Ложь).Булево не существует. При передаче значения типа Булево из внешнего OLE-источника платформа 1С 7.7 интерпретирует Истина как -1, а Ложь — как 0.Когда мы пишем условие Пока ВыборкаБД.Следующий() = 1, система сравнивает возвращаемое значение (которое равно -1) с единицей. Поскольку -1 <> 1, условие сразу принимает значение «Ложь», и цикл завершается, так и не начавшись.
Чтобы обойти это ограничение, мы можем использовать несколько вариантов написания логического условия в цикле. Рассмотрим наиболее надежные способы.
Способ 1. Проверка на неравенство нулю (рекомендуемый способ)
Поскольку значение Ложь в 1С 7.7 всегда преобразуется в 0, а любое истинное значение будет отличным от нуля, самым безопасным вариантом является проверка на неравенство нулю:
ВыборкаБД = ЗапросБД.Выполнить().Выбрать();
Пока ВыборкаБД.Следующий() <> 0 Цикл
// Получаем простые типы данных (числа, строки, даты)
НомерДок = ВыборкаБД.Номер;
ДатаДок = ВыборкаБД.Дата;
Сообщить("Обрабатывается документ № " + НомерДок + " от " + ДатаДок);
КонецЦикла;
Способ 2. Проверка на равенство -1
Вы можете явно указать проверку на -1, так как именно это число возвращает OLE-сервер при значении Истина:
Пока ВыборкаБД.Следующий() = -1 Цикл
// Код обработки
КонецЦикла;
Оба этих способа заставят 1С 7.7 корректно распознавать шаги выборки и заходить в тело цикла.
При передаче данных между базами важно понимать, как платформы преобразуют типы данных «на лету». Напрямую через COM-соединение без искажений передаются только простые типы: Число, Строка и Дата.
Если в запросе 1С 8 вы выбираете ссылочные типы (например, Ссылка на справочник или документ), в 1С 7.7 эти поля придут как сложные COM-объекты. Вы не сможете вывести их на форму или записать в базу 1С 7.7 напрямую без предварительного преобразования.
Рассмотрим правила, которые помогут избежать ошибок при работе с типами данных:
Представление(). На стороне 1С 7.7 вы получите готовую строку.Дата не содержит времени (время хранится отдельно в документах). При передаче даты из 1С 7.7 в параметры запроса 1С 8 используйте явное конструирование объектов даты, чтобы не потерять время документа (например, конец дня 23:59:59).Булево (например, отбор по реквизиту Проведен), обычные числа 1 или 0 из 1С 7.7 могут вызвать ошибку несоответствия типов. Используйте десериализацию системной строки 1С 8 через OLE:
// Создаем оригинальные булевы объекты платформы 1С 8
Истина8 = v8.ValueFromStringInternal("{""B"",1}");
Ложь8 = v8.ValueFromStringInternal("{""B"",0}");
// Передаем параметр в запрос
ЗапросБД.УстановитьПараметр("ТолькоПроведенные", Истина8);
Ниже приведем готовый к использованию шаблон кода для внешней обработки 1С 7.7, который подключается к базе 1С 8 (например, ЗУП или Бухгалтерии), выполняет запрос с параметрами дат и корректно обходит результаты.
Процедура ВыполнитьЗапросК8()
// 1. Инициализируем COM-соединение
Попытка
БазаОЛЕ = СоздатьОбъект("V83.COMConnector");
Исключение
Предупреждение("Не удалось создать COMConnector. Проверьте регистрацию библиотеки comcntr.dll!");
Возврат;
КонецПопытки;
// Строка подключения (файловый вариант базы 1С 8)
СтрокаПодключения = "File=""" + СокрЛП(ПутьКБазе) + """; Usr=""" + СокрЛП(Пользователь) + """; Pwd=""" + СокрЛП(Пароль) + """";
Попытка
v8 = БазаОЛЕ.Connect(СтрокаПодключения);
Исключение
Предупреждение("Ошибка подключения к базе 1С 8: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Сообщить("Подключение установлено успешно!");
// 2. Создаем объект запроса в контексте 1С 8
ЗапросБД = v8.NewObject("Запрос");
// Задаем текст запроса (используем синтаксис 1С 8)
ЗапросБД.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Номер КАК Номер,
| Док.Дата КАК Дата,
| Представление(Док.Организация) КАК ОрганизацияПредставление
|ИЗ
| Документ.ОтражениеЗарплатыВБухучете КАК Док
|ГДЕ
| Док.Дата МЕЖДУ &НачДата И &КонДата И Док.ПометкаУдаления = ЛОЖЬ";
// 3. Устанавливаем параметры дат с учетом специфики 1С 8 (передаем дату со временем)
// Конструируем дату начала дня (00:00:00) и конца дня (23:59:59) на стороне 1С 8
НачДата8 = v8.Date(Год(НачДата), Месяц(НачДата), Число(НачДата), 0, 0, 0);
КонДата8 = v8.Date(Год(КонДата), Месяц(КонДата), Число(КонДата), 23, 59, 59);
ЗапросБД.УстановитьПараметр("НачДата", НачДата8);
ЗапросБД.УстановитьПараметр("КонДата", КонДата8);
// 4. Выполняем запрос
РезультатЗапроса = ЗапросБД.Выполнить();
ВыборкаБД = РезультатЗапроса.Выбрать();
КоличествоЗаписей = ВыборкаБД.Количество();
Сообщить("Найдено документов: " + КоличествоЗаписей);
// Если записей нет, прерываем обработку
Если КоличествоЗаписей = 0 Тогда
v8 = ""; // Освобождаем память
Возврат;
КонецЕсли;
// 5. Обходим выборку (проверка на неравенство нулю!)
Пока ВыборкаБД.Следующий() <> 0 Цикл
// Получаем значения простых типов напрямую
ДокНомер = ВыборкаБД.Номер;
ДокДата = ВыборкаБД.Дата;
ОргИмя = ВыборкаБД.ОрганизацияПредставление;
// Ссылочный объект 1С 8 (будет OLE-объектом в 1С 7.7)
ДокСсылка = ВыборкаБД.Ссылка;
Сообщить("Документ № " + ДокНомер + " от " + ДокДата + " (" + ОргИмя + ")");
КонецЦикла;
// 6. Обязательно очищаем ссылки на OLE-объекты для предотвращения утечек памяти
ВыборкаБД = "";
РезультатЗапроса = "";
ЗапросБД = "";
v8 = ""; // Закрываем сессию подключения
Сообщить("Обработка данных завершена.");
КонецПроцедуры
Построчный обход выборки OLE-запроса в цикле Пока ВыборкаБД.Следующий() <> 0 работает медленно, если количество записей исчисляется тысячами. Каждый вызов метода Следующий() и каждое чтение свойства (например, ВыборкаБД.Номер) — это отдельный сетевой/процессный вызов (круг передачи данных между приложениями), требующий времени.
Для оптимизации скорости работы рекомендуем использовать следующие приемы:
ТабЗнач8 = ЗапросБД.Выполнить().Выгрузить();. Хотя объект по-прежнему останется внешним, доступ к строкам по индексам в некоторых случаях работает быстрее, чем итератор выборки.V83.COMConnector перед объектом V83.Application. COMConnector не инициализирует тяжелый графический интерфейс пользователя 1С 8, работает в разы быстрее и потребляет значительно меньше оперативной памяти сервера.