При интеграции «1С:Предприятия» со сторонними базами данных через механизм Внешние источники данных разработчики нередко сталкиваются с ситуацией: данные успешно записываются в таблицу внешней СУБД, однако последующий запрос в том же сеансе возвращает пустой результат. Кажется, что база «не успела» зафиксировать изменения или требуется программная пауза. Разберем по шагам, почему это происходит и как правильно организовать чтение и запись данных во внешние таблицы.
Главная и наиболее частая причина отсутствия данных при выборке — несовпадение типов значений, передаваемых в параметры запроса. В отличие от стандартных таблиц базы 1С, где платформа может сглаживать некоторые различия типов или преобразовывать их, запрос к внешнему источнику транслируется через ODBC-драйвер непосредственно в синтаксис внешней СУБД (например, MySQL, PostgreSQL или MS SQL Server).
Если во внешней таблице поле имеет числовой тип (например, INT или BIGINT), а в параметрах запроса 1С передается строка (например, "123" вместо числа 123), СУБД выполняет строгое сравнение. В результате условие ГДЕ не выполняется, и выборка оказывается пустой.
Посмотрим на пример функции получения данных, где возникает ошибка:
&НаСервере
Функция ПолучитьДопИнфуПоПользователямMoodle(UserId)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| mdl_user_info_data.data,
| mdl_user_info_data.fieldid
|ИЗ
| ВнешнийИсточникДанных.ВнешнийИсточникДанных3.Таблица.mdl_user_info_data КАК mdl_user_info_data
|ГДЕ
| mdl_user_info_data.userid = &userid";
// Если UserId пришел со строковым типом, внешняя СУБД не вернет запись
Запрос.УстановитьПараметр("userid", Число(UserId));
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Проанализируем это исправление: явное приведение значения к типу Число перед передачей в параметр &userid гарантирует корректную трансляцию условия в нативный SQL-запрос целевой базы данных.
Нередко разработчики пытаются обрамлять запись во внешний источник стандартными методами платформы:
НачатьТранзакцию();
// Запись во внешнюю таблицу...
ЗафиксироватьТранзакцию();
Важно понимать: системные методы НачатьТранзакцию() и ЗафиксироватьТранзакцию() управляют транзакцией только основной базы данных 1С. Они не оказывают никакого влияния на внешнюю СУБД.
Для управления транзакциями во внешних базах платформа предоставляет отдельные методы менеджера источника:
ВнешниеИсточникиДанных.<ИмяИсточника>.НачатьТранзакцию() — открывает явную транзакцию в подключенной СУБД.ВнешниеИсточникиДанных.<ИмяИсточника>.ЗафиксироватьТранзакцию() — подтверждает внесенные изменения.ВнешниеИсточникиДанных.<ИмяИсточника>.ОтменитьТранзакцию() — откатывает изменения при ошибке.ВнешниеИсточникиДанных.<ИмяИсточника>.ТранзакцияАктивна() — позволяет проверить текущий статус.Если явная транзакция внешнего источника не используется, вызов метода Записать() объекта внешней таблицы автоматически открывает и сразу фиксирует транзакцию на уровне внешнего соединения. Задержек записи на стороне СУБД при успешном выполнении метода не происходит, поэтому искусственные паузы программно делать не нужно.
При записи нового объекта внешней таблицы разработчики часто обращают внимание, что свойство ВнешИст.Ссылка принимает вид <Объект не найден>. Рассмотрим причину такого поведения:
Во внешних базах первичный ключ часто генерируется автоматически СУБД (механизмы AUTO_INCREMENT, IDENTITY или триггеры). В метаданных 1С такие поля помечаются флагом «Только чтение», чтобы платформа не пыталась передать в них пустое значение. При вызове Записать() платформа 1С в ряде конфигураций драйверов не считывает обратно сгенерированный базой идентификатор. Из-за этого ключевые поля в памяти остаются незаполненными, а рассчитываемая платформой ссылка указывает на несуществующую комбинацию ключей. Сама запись при этом физически успешно сохраняется в СУБД.
Посмотрим на типовой алгоритм синхронизации, выполняющий запись и чтение внутри цикла:
Для Каждого Студент Из Объект.Синхронизация Цикл
Если Студент.Spec = "" Тогда
ВнешИст = ВнешниеИсточникиДанных.ВнешнийИсточникДанных3.Таблицы.mdl_user_info_data.СоздатьОбъект();
ВнешИст.data = Студент.Направление.Наименование;
ВнешИст.dataformat = 0;
ВнешИст.fieldid = 2;
ВнешИст.userid = Число(Студент.UserId);
ВнешИст.Записать();
// Запрос внутри каждой итерации цикла создает чрезмерную сетевую нагрузку
ВнешДанныеПоСтуд = ПолучитьДопИнфуПоПользователямMoodle(Студент.UserId);
// ...
КонецЕсли;
КонецЦикла;
Выполнение запросов к внешней СУБД на каждой итерации цикла критически замедляет работу за счет сетевых задержек ODBC. Для оптимизации рекомендуется собирать идентификаторы в массив и выполнять один пакетный запрос с условием ГДЕ mdl_user_info_data.userid В (&МассивИдентификаторов), после чего обрабатывать полученные данные в оперативной памяти.