При интеграции 1С со сторонними сервисами (в частности, со шлюзами отправки SMS через прямое подключение к базе данных MySQL) разработчики часто сталкиваются с ситуацией, когда запрос возвращает не последнее актуальное значение, а старые или произвольные данные. Разберем причины такого поведения и настроим надежное получение информации.
Рассмотрим типовой сценарий: для подключения к внешней СУБД используется COM-объект ADODB.Connection и драйвер ODBC, а выборка выполняется через ADODB.RecordSet. В исходном варианте кода отправляется простой запрос без сортировки:
RecordSet.Open("select balance from mylogin", Соединение, 3);
Если Не RecordSet.EOF() Тогда
RecordSet.MoveLast();
Баланс = RecordSet.Fields("balance").Value;
КонецЕсли;
Проанализируем, почему такой подход приводит к ошибке:
ORDER BY. Сервер возвращает строки в том порядке, в котором ему удобнее их прочитать с диска или из кэша. Перемещение курсора в конец выборки через RecordSet.MoveLast() переводит указатель на последнюю строку полученного набора, но эта строка не обязательно является последней по времени добавления.SELECT balance FROM mylogin заставляет СУБД и сеть передавать в 1С весь журнал сообщений за все время работы. Это приводит к избыточной нагрузке на канал связи и замедляет работу системы.balance фиксирует остаток средств на момент отправки конкретного SMS-сообщения. Чтобы получить актуальный остаток, требуется найти именно самую свежую запись по идентификатору или дате.Разберем по шагам, как правильно составить запрос на стороне сервера 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();
Исключение
// Игнорируем ошибку закрытия, если соединение уже разорвано
КонецПопытки;
Возврат Баланс;
КонецФункции
Рассмотрим важные нюансы, которые необходимо учитывать при эксплуатации данного кода:
ADODB.Connection и ADODB.RecordSet функционируют исключительно в операционной системе Windows. Если сервер 1С работает под управлением Linux, использование COM-объектов вызовет исключение.rphost.exe — 32-bit или 64-bit).LIMIT 1 нет необходимости передавать параметр типа курсора 3 (статический курсор adOpenStatic). Достаточно использовать курсор только для чтения вперед (0), что экономит оперативную память на сервере.Если требуется получать реальный баланс учетной записи без привязки к отправленным SMS (например, после пополнения счета в личном кабинете), а также обеспечить работу на серверах под управлением Linux, рекомендуется отказаться от прямого подключения к базе в пользу официального HTTP/REST API сервиса.
В платформе 1С для этого используются штатные объекты HTTPСоединение и HTTPЗапрос, которые не требуют установки внешних ODBC-драйверов и не блокируются сетевыми экранами на уровне нестандартных портов СУБД (порт 3306).