Почему запрос к внешнему источнику данных не находит только что записанные данные?

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

При интеграции «1С:Предприятия» со сторонними базами данных через механизм Внешние источники данных разработчики нередко сталкиваются с ситуацией: данные успешно записываются в таблицу внешней СУБД, однако последующий запрос в том же сеансе возвращает пустой результат. Кажется, что база «не успела» зафиксировать изменения или требуется программная пауза. Разберем по шагам, почему это происходит и как правильно организовать чтение и запись данных во внешние таблицы.

Причина 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-запрос целевой базы данных.

Причина 2. Особенности управления транзакциями во внешних источниках

Нередко разработчики пытаются обрамлять запись во внешний источник стандартными методами платформы:


НачатьТранзакцию();
// Запись во внешнюю таблицу...
ЗафиксироватьТранзакцию();

Важно понимать: системные методы НачатьТранзакцию() и ЗафиксироватьТранзакцию() управляют транзакцией только основной базы данных 1С. Они не оказывают никакого влияния на внешнюю СУБД.

Для управления транзакциями во внешних базах платформа предоставляет отдельные методы менеджера источника:

  1. ВнешниеИсточникиДанных.<ИмяИсточника>.НачатьТранзакцию() — открывает явную транзакцию в подключенной СУБД.
  2. ВнешниеИсточникиДанных.<ИмяИсточника>.ЗафиксироватьТранзакцию() — подтверждает внесенные изменения.
  3. ВнешниеИсточникиДанных.<ИмяИсточника>.ОтменитьТранзакцию() — откатывает изменения при ошибке.
  4. ВнешниеИсточникиДанных.<ИмяИсточника>.ТранзакцияАктивна() — позволяет проверить текущий статус.

Если явная транзакция внешнего источника не используется, вызов метода Записать() объекта внешней таблицы автоматически открывает и сразу фиксирует транзакцию на уровне внешнего соединения. Задержек записи на стороне СУБД при успешном выполнении метода не происходит, поэтому искусственные паузы программно делать не нужно.

Почему в свойстве Ссылка отображается «Объект не найден»?

При записи нового объекта внешней таблицы разработчики часто обращают внимание, что свойство ВнешИст.Ссылка принимает вид <Объект не найден>. Рассмотрим причину такого поведения:

Во внешних базах первичный ключ часто генерируется автоматически СУБД (механизмы 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 В (&МассивИдентификаторов), после чего обрабатывать полученные данные в оперативной памяти.

← На главную