Как правильно подключиться к базе данных Oracle из 1С через ADO и обработать полученные данные?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Интеграция сторонних баз данных с платформой 1С — это частая задача при автоматизации бизнес-процессов. СУБД Oracle широко распространена в корпоративном секторе, и обмен данными с ней часто реализуется с помощью технологии ADO (ActiveX Data Objects). Однако при написании кода разработчики регулярно сталкиваются со специфическими ошибками: от некорректного обхода набора записей до проблем с преобразованием типов дат и блокировками транзакций.

В этой статье мы подробно разберем, как правильно организовать подключение к Oracle, как обойти все строки результата запроса без потери данных, как корректно передавать параметры дат в запросы и как производить обратную запись (обновление статусов) во внешнюю базу данных. Если требуется настроить передачу без ручного написания кода обмена, можно использовать подсистему выгрузки данных во внешнюю базу SQL.

Почему возвращается только одна строка из таблицы Oracle?

Рассмотрим типичную ошибку начинающих разработчиков при работе с ADO. В исходном коде автора была предпринята следующая попытка получить данные:


Попытка
    Записи = Новый ComObject("ADODB.RecordSet");
    Записи.Open(ТекстЗапроса, Соединение);
    СтрокаXML = Записи.Fields; 
    Возврат СтрокаXML;
Исключение
    Сообщить(ОписаниеОшибки);
КонецПопытки;

При таком подходе функция возвращает только первую строку выборки. Выясним причину этой проблемы. Объект RecordSet представляет собой курсор, который указывает на текущую запись в наборе данных. Свойство Fields возвращает коллекцию полей именно для текущей (в данном случае — самой первой) записи. Для того чтобы получить все строки таблицы, необходимо программно перемещать курсор от первой записи к последней.

Для последовательного обхода набора записей мы должны использовать цикл Пока НЕ Записи.EOF (End Of File) и метод перехода к следующей строке MoveNext(). Разберем правильную структуру обхода выборки:


Пока НЕ Записи.EOF Цикл
    // Обработка данных текущей строки
    ЗначениеПоля = Записи.Fields("ИмяПоля").Value;
    
    // Переход к следующей записи выборки
    Записи.MoveNext();
КонецЦикла;

Если пропустить вызов метода MoveNext(), программа войдет в бесконечный цикл, так как указатель никогда не достигнет конца файла (свойства EOF).

Заполнение Таблицы Значений из ADO Recordset

Для удобной работы с данными в среде 1С полученные из Oracle строки лучше всего выгрузить во временную таблицу значений. Посмотрим на практический пример динамического формирования колонок и строк таблицы значений на основе полей Recordset:


ТаблицаРезультат = Новый ТаблицаЗначений;

// Сначала динамически создаем колонки в Таблице Значений по структуре полей Oracle
Для Каждого Поле Из Записи.Fields Цикл
    ТаблицаРезультат.Колонки.Добавить(Поле.Name);
КонецЦикла;

// Начинаем обход записей СУБД Oracle
Пока НЕ Записи.EOF Цикл
    НоваяСтрока = ТаблицаРезультат.Добавить();
    
    // Заполняем поля строки
    Для Каждого Поле Из Записи.Fields Цикл
        НоваяСтрока[Поле.Name] = Поле.Value;
    КонецЦикла;
    
    Записи.MoveNext();
КонецЦикла;

Такой подход универсален: вам не нужно заранее знать точную структуру таблицы в Oracle, колонки будут созданы автоматически с теми же именами, которые возвращает SQL-запрос.

Специфика работы с датами в Oracle: функция TO_DATE

Еще одна распространенная проблема при интеграции 1С и Oracle связана с фильтрацией данных по периоду. Попытка передать дату напрямую из 1С в текст SQL-запроса через обычное сложение строк неизбежно приведет к ошибке СУБД:


// ТАК ДЕЛАТЬ НЕЛЬЗЯ - приведет к ошибке СУБД Oracle
ТекстЗапроса = "SELECT * FROM DOGOVOR_LOG_1C WHERE DATE_OP BETWEEN ('" + ДатаНачала + "') AND ('" + ДатаОкончания + "')";

СУБД Oracle не умеет автоматически интерпретировать строковое представление даты платформы 1С. Для преобразования строки в валидный тип даты в Oracle существует встроенная функция TO_DATE. Она принимает два основных параметра: строковое значение даты и маску формата этой даты.

Проанализируем пример правильного построения запроса с выбором данных за текущий день:


// Форматируем даты в 1С к текстовому виду "ДД.ММ.ГГГГ ЧЧ:ММ:СС"
СтрокаДатаНачала    = Формат(НачалоДня(ТекущаяДата()), "ДФ='dd.MM.yyyy HH:mm:ss'");
СтрокаДатаОкончания = Формат(КонецДня(ТекущаяДата()), "ДФ='dd.MM.yyyy HH:mm:ss'");

// Собираем текст SQL-запроса с использованием TO_DATE и маски формата DD.MM.YYYY HH24:MI:SS
ТекстЗапроса = "SELECT * 
               |FROM ASU2000_COMERC.DOGOVOR_LOG_1C 
               |WHERE DATE_OP BETWEEN TO_DATE('" + СтрокаДатаНачала + "', 'DD.MM.YYYY HH24:MI:SS') 
               |                  AND TO_DATE('" + СтрокаДатаОкончания + "', 'DD.MM.YYYY HH24:MI:SS')";

Обратите внимание на соответствие маски формата в 1С и маски формата в Oracle: в Oracle часы обозначаются как HH24 (24-часовой формат), минуты как MI, а секунды как SS.

Запись изменений обратно в Oracle и оптимизация производительности

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

В коде автора из темы форума была допущена грубая синтаксическая и логическая ошибка:


// Код автора с ошибками:
ТекстЗапросаЗаписиВИАС = "UPDATA ASU2000_COMERC.DOGOVOR_LOG_1C SET PROCESSED = '1' ')";

Ошибки в данном коде:

  1. Опечатка в ключевом слове: написано UPDATA вместо UPDATE.
  2. Лишние символы скобки и кавычки в конце запроса.
  3. Самое опасное: отсутствует предложение WHERE ID = .... Если запустить такой запрос на выполнение, СУБД обновит поле PROCESSED для всех записей в таблице, затерев важные исторические данные!

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

Для максимальной производительности СУБД и защиты от уязвимостей SQL-инъекций следует использовать параметризованные запросы с помощью объекта ADODB.Command и связываемых переменных (Bind Variables). Это позволит СУБД Oracle кэшировать план выполнения запроса.

Рассмотрим пример комплексного решения:


// 1. Создаем таблицу для данных 1С и массив для ИД обработанных записей
МассивОбработанныхИД = Новый Массив;
ТаблицаЛога = Новый ТаблицаЗначений;
// ... (Заполнение структуры ТаблицыЛога) ...

Пока НЕ Записи.EOF Цикл
    // Заполняем строку нашей ТаблицыЗначений
    НоваяСтрока = ТаблицаЛога.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Записи.Fields); // переносим данные
    
    // Если запись еще не обработана внешней системой
    Если Записи.Fields("PROCESSED").Value = 0 Тогда
        МассивОбработанныхИД.Добавить(Записи.Fields("ID").Value);
    КонецЕсли;
    
    Записи.MoveNext();
КонецЦикла;

// 2. Обновляем статусы в Oracle с помощью параметризованного запроса в транзакции
Если МассивОбработанныхИД.Количество() > 0 Тогда
    
    // Открываем транзакцию для ускорения множественной записи
    Соединение.BeginTrans();
    
    Попытка
        Команда = Новый COMОбъект("ADODB.Command");
        Команда.ActiveConnection = Соединение;
        
        // Используем символ "?" в качестве параметра
        Команда.CommandText = "UPDATE ASU2000_COMERC.DOGOVOR_LOG_1C SET PROCESSED = 1 WHERE ID = ?";
        
        Для Каждого ТекущийИД Из МассивОбработанныхИД Цикл
            // Очищаем предыдущие параметры перед каждым шагом
            Команда.Parameters.Delete(0); 
            
            // Создаем параметр: Имя, Тип (3 = adInteger), Направление (1 = adParamInput), Размер, Значение
            НовыйПараметр = Команда.CreateParameter("ID", 3, 1, , ТекущийИД);
            Команда.Parameters.Append(НовыйПараметр);
            
            // Выполняем обновление одной записи
            Команда.Execute();
        КонецЦикла;
        
        // Фиксируем транзакцию, если все прошло успешно
        Соединение.CommitTrans();
        Сообщить("Статусы успешно обновлены в Oracle.");
        
    Исключение
        // Откатываем транзакцию в случае любой ошибки
        Соединение.RollbackTrans();
        Сообщить("Ошибка обновления статусов: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецЕсли;

Важные системные нюансы интеграции

При интеграции 1С и внешних баз данных через ADO важно помнить о системных требованиях и архитектурных ограничениях технологии:

  1. Проблема разрядности (32-bit / 64-bit): Код с директивой &НаСервере исполняется непосредственно на сервере "1С:Предприятия". Если ваш сервер 1С является 64-разрядным (что стандартно для большинства современных рабочих систем), то на сервере обязательно должен быть установлен 64-разрядный Oracle Client (например, ODAC или Oracle Instant Client). 32-битный клиент Oracle не позволит 64-битному процессу rphost создать COM-объект ADODB.Connection, что приведет к ошибке "Класс не зарегистрирован".
  2. Операционная система сервера: Использование COM-объектов жестко привязывает серверный код к операционной системе Windows. Если ваш сервер 1С работает под управлением Linux, использование COMObject("ADODB.Connection") завершится ошибкой, так как COM-технология там не поддерживается.
  3. Утечки оперативной памяти: COM-объекты, создаваемые на сервере 1С, могут оставаться в оперативной памяти процесса rphost даже после завершения работы функции. Чтобы избежать утечек памяти, всегда принудительно закрывайте соединения и наборы записей, особенно в блоках Исключение, и обнуляйте ссылки на них:

// Пример правильного освобождения ресурсов в конце процедуры
Попытка
    Записи.Close();
    Соединение.Close();
Исключение
    // Игнорируем ошибки при закрытии уже закрытых соединений
КонецПопытки;

Записи = Неопределено;
Соединение = Неопределено;
Команда = Неопределено;

Современная альтернатива: Внешние источники данных

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

Преимущества использования Внешних источников данных перед ADO:

← На главную