Как в 1С 8.3 получить актуальный баланс из MySQL через ADODB?

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

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

Анализ проблемы: почему метод MoveLast() не возвращает последнюю запись

Рассмотрим типовой сценарий: для подключения к внешней СУБД используется COM-объект ADODB.Connection и драйвер ODBC, а выборка выполняется через ADODB.RecordSet. В исходном варианте кода отправляется простой запрос без сортировки:


RecordSet.Open("select balance from mylogin", Соединение, 3);
Если Не RecordSet.EOF() Тогда
    RecordSet.MoveLast();
    Баланс = RecordSet.Fields("balance").Value;
КонецЕсли;

Проанализируем, почему такой подход приводит к ошибке:

  1. Отсутствие гарантированного порядка строк в реляционной СУБД. Стандарт SQL не гарантирует порядок выборки записей, если в запросе явно не указана конструкция ORDER BY. Сервер возвращает строки в том порядке, в котором ему удобнее их прочитать с диска или из кэша. Перемещение курсора в конец выборки через RecordSet.MoveLast() переводит указатель на последнюю строку полученного набора, но эта строка не обязательно является последней по времени добавления.
  2. Неэффективный расход ресурсов и памяти. Запрос SELECT balance FROM mylogin заставляет СУБД и сеть передавать в 1С весь журнал сообщений за все время работы. Это приводит к избыточной нагрузке на канал связи и замедляет работу системы.
  3. Особенности архитектуры таблиц шлюза. В подобных таблицах колонка balance фиксирует остаток средств на момент отправки конкретного SMS-сообщения. Чтобы получить актуальный остаток, требуется найти именно самую свежую запись по идентификатору или дате.

Решение 1: Оптимизация SQL-запроса с сортировкой и ограничением выборки

Разберем по шагам, как правильно составить запрос на стороне сервера MySQL. Чтобы получить ровно одну последнюю запись, добавим явную сортировку по первичному ключу таблицы (id) в порядке убывания и ограничим результат одной строкой с помощью LIMIT 1.

Посмотрим на исправленный и оптимизированный пример функции 1С:


Функция ПолучитьБалансSMS(Сервер, БазаДанных, Логин, Пароль, Кодировка = "utf8")
    
    Перем Соединение, RecordSet, Баланс;
    Баланс = 0;
    
    СтрокаСоединения = "DRIVER={MySQL ODBC 3.51 Driver};"
        + "SERVER=" + Сервер + ";"
        + "DATABASE=" + БазаДанных + ";"
        + "CHARSET=" + Кодировка + ";"
        + "UID=" + Логин + ";"
        + "PWD=" + Пароль + ";";
    
    Попытка
        Соединение = Новый COMОбъект("ADODB.Connection");
        Соединение.Open(СтрокаСоединения);
        // Устанавливаем кодировку сессии для корректной работы с кириллицей
        Соединение.Execute("SET NAMES " + Кодировка + ";");
    Исключение
        ЗаписьЖурналаРегистрации("Интеграция SMS", УровеньЖурналаРегистрации.Ошибка, , , 
            "Ошибка подключения к MySQL: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
    
    Попытка
        RecordSet = Новый COMОбъект("ADODB.RecordSet");
        
        // Запрашиваем только одну последнюю запись по первичному ключу
        ТекстЗапроса = "SELECT balance FROM mylogin ORDER BY id DESC LIMIT 1";
        
        // Используем последовательный курсор по умолчанию (adOpenForwardOnly = 0, adLockReadOnly = 1)
        RecordSet.Open(ТекстЗапроса, Соединение, 0, 1);
        
        Если Не RecordSet.EOF() И Не RecordSet.BOF() Тогда
            Баланс = RecordSet.Fields("balance").Value;
        КонецЕсли;
        
        RecordSet.Close();
    Исключение
        ЗаписьЖурналаРегистрации("Интеграция SMS", УровеньЖурналаРегистрации.Ошибка, , , 
            "Ошибка выполнения запроса к MySQL: " + ОписаниеОшибки());
    КонецПопытки;
    
    Попытка
        Соединение.Close();
    Исключение
        // Игнорируем ошибку закрытия, если соединение уже разорвано
    КонецПопытки;
    
    Возврат Баланс;
    
КонецФункции

Особенности и технические ограничения COM и ODBC в 1С

Рассмотрим важные нюансы, которые необходимо учитывать при эксплуатации данного кода:

  1. Кроссплатформенность. Объекты ADODB.Connection и ADODB.RecordSet функционируют исключительно в операционной системе Windows. Если сервер 1С работает под управлением Linux, использование COM-объектов вызовет исключение.
  2. Разрядность драйверов. При выполнении кода на стороне сервера (в серверном модуле или регламентном задании) разрядность установленного драйвера MySQL ODBC должна в точности соответствовать разрядности рабочего процесса 1С:Предприятия (rphost.exe — 32-bit или 64-bit).
  3. Тип курсора. При получении одной записи с конструкцией LIMIT 1 нет необходимости передавать параметр типа курсора 3 (статический курсор adOpenStatic). Достаточно использовать курсор только для чтения вперед (0), что экономит оперативную память на сервере.

Решение 2: Переход на кроссплатформенный HTTP API

Если требуется получать реальный баланс учетной записи без привязки к отправленным SMS (например, после пополнения счета в личном кабинете), а также обеспечить работу на серверах под управлением Linux, рекомендуется отказаться от прямого подключения к базе в пользу официального HTTP/REST API сервиса.

В платформе 1С для этого используются штатные объекты HTTPСоединение и HTTPЗапрос, которые не требуют установки внешних ODBC-драйверов и не блокируются сетевыми экранами на уровне нестандартных портов СУБД (порт 3306).

← На главную