Почему двоичные данные из внешнего источника Oracle ломают кодировку строк в 1С и как это исправить?

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

При интеграции 1С с внешними базами данных, такими как Oracle, через механизм ВнешниеИсточникиДанных, мы часто сталкиваемся с нетривиальным поведением драйверов и платформы. Одной из самых загадочных и неприятных проблем является ситуация, когда при попытке прочитать таблицу, содержащую и текстовые описания, и двоичные данные (файлы, сканы, BLOB), кириллические строки превращаются в знаки вопроса («??????»), в то время как латиница и цифры отображаются корректно.

В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем поведение ODBC-драйверов и рассмотрим несколько способов решения задачи — от изменения текста запроса до архитектурного разделения данных.

Суть проблемы: конфликт типов данных в выборке

Давайте смоделируем ситуацию. У нас есть внешняя база данных Oracle, в которой хранится таблица с документами. Структура таблицы включает:

  1. Уникальный идентификатор (ID).
  2. Наименование продукции (строка, например, NVARCHAR2).
  3. Данные файла (двоичные данные, например, BLOB или LONG RAW).

Мы настраиваем внешний источник данных в 1С, создаем форму списка и форму объекта. В форме списка (динамический список) все отображается корректно: мы видим русские названия продукции. Однако, стоит нам попытаться получить данные программно через запрос или открыть форму объекта, где выбираются все поля, как поле «Наименование» превращается в набор вопросительных знаков.

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

Причины возникновения ошибки

Прежде чем переходить к лечению, давайте поставим диагноз. Существует несколько технических причин такого поведения:

  1. Порядок полей в буфере ODBC. Многие ODBC-драйверы (особенно старые версии драйверов Oracle) имеют ограничения на обработку полей переменной длины и больших объектов (LOB). Если «тяжелое» поле (BLOB) идет в выборке перед текстовым полем, драйвер может некорректно сместить указатель чтения для следующего поля, из-за чего байты строки интерпретируются неверно.
  2. Настройки NLS_LANG. Несоответствие кодировки клиента (сервера 1С) и сервера БД Oracle. Переменная окружения NLS_LANG должна точно соответствовать кодировке данных в базе.
  3. Особенности работы платформы 1С. Платформа может кэшировать метаданные внешнего источника. Если тип поля на стороне Oracle изменился, а в 1С старое описание, возможны ошибки чтения.

Рассмотрим способы решения этой проблемы, от простых к более сложным и надежным.

Способ 1: Архитектурное разделение (Рекомендуемый)

Наиболее надежным решением, к которому приходят опытные разработчики, является отказ от выборки двоичных данных в одном запросе с описательными полями. Двоичные данные — это «тяжелый» контент, который редко нужен пользователю непосредственно при просмотре карточки или списка. Обычно файл нужен только в момент скачивания или сохранения на диск.

Давайте реализуем подход, при котором мы не тянем ДвоичныеДанные в основную форму, а получаем их отдельным запросом только по требованию.

Шаг 1. Настройка внешнего источника

В дереве метаданных во внешнем источнике данных лучше иметь две сущности (даже если физически это одна таблица, можно добавить ее дважды с разными именами или использовать View на стороне Oracle):

  1. ТаблицаДокументы — содержит все поля кроме двоичных данных.
  2. ТаблицаКонтент — содержит только ID и поле с двоичными данными.

Шаг 2. Реализация скачивания файла

На форме объекта мы отображаем только строковые данные. Для работы с файлом добавляем команду «Сохранить файл». В обработчике этой команды мы выполняем отдельный запрос к источнику, выбирая данные по ID.

Пример программного кода для получения данных:


&НаСервере
Функция ПолучитьФайлИзOracle(ИдентификаторФайла)
    
    Запрос = Новый Запрос;
    // Обращаемся к таблице, где мы намеренно оставили двоичные данные
    // или используем ту же таблицу, но выбираем ТОЛЬКО двоичные данные
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ДанныеИзОракла.Данные_документа КАК ДвоичныеДанные
        |ИЗ
        |   ВнешнийИсточникДанных.ВнешняяБаза.Таблица.Документы_Интегрослоя КАК ДанныеИзОракла
        |ГДЕ
        |   ДанныеИзОракла.ID = &ID";
    
    Запрос.УстановитьПараметр("ID", ИдентификаторФайла);
    
    Результат = Запрос.Выполнить();
    Если Не Результат.Пустой() Тогда
        Выборка = Результат.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.ДвоичныеДанные;
    КонецЕсли;
    
    Возврат Неопределено;

КонецФункции

Такой подход решает сразу две проблемы: устраняет ошибку кодировки (так как типы не смешиваются) и оптимизирует производительность (мы не гоняем по сети тяжелые файлы при простом открытии формы).

Способ 2: Изменение порядка полей в запросе

Если разделение данных невозможно и вам необходимо получить все данные одним запросом, попробуйте изменить порядок полей. Это специфическая особенность работы драйверов ODBC с LOB-типами.

Давайте посмотрим на "проблемный" запрос:


ВЫБРАТЬ ПЕРВЫЕ 1
    ДанныеИзОракла.Данные_документа КАК Ссылка, // Двоичные данные идут первыми
    ДанныеИзОракла.Наименование_продукции КАК Наименование_продукции
ИЗ
    ВнешнийИсточникДанных.ВнешняяБаза.Таблица.Документы_Интегрослоя КАК ДанныеИзОракла

В этом случае буфер чтения заполняется большим объемом данных первого поля, и на чтении второго поля драйвер "сбивается", выдавая вопросительные знаки. Попробуем переписать запрос так, чтобы ДвоичныеДанные всегда были в самом конце списка выборки SELECT.


ВЫБРАТЬ ПЕРВЫЕ 1
    ДанныеИзОракла.Наименование_продукции КАК Наименование_продукции, // Сначала строки
    ДанныеИзОракла.Данные_документа КАК Ссылка // Двоичные данные в конце
ИЗ
    ВнешнийИсточникДанных.ВнешняяБаза.Таблица.Документы_Интегрослоя КАК ДанныеИзОракла

Во многих случаях, особенно при прямом использовании ODBC, это простое действие восстанавливает нормальное отображение кириллицы.

Способ 3: Использование представлений (Views) на стороне СУБД

Если вы имеете доступ к администрированию базы данных Oracle, наиболее "чистым" вариантом будет создание специального представления (View) для 1С.

Вместо того чтобы заставлять 1С мучиться с "сырой" таблицей, создайте View, которое:

  1. Явно преобразует типы данных, если это необходимо.
  2. Исключает колонку с двоичными данными для основного списка.

Затем в 1С вы подключаете это представление как таблицу внешнего источника. А для получения самого файла используете прямую таблицу, обращаясь к ней точечно по ID, как описано в Способе 1. Это позволит избежать создания дублей данных и обеспечит стабильную работу интерфейса 1С.

Способ 4: Настройка драйвера и окружения

Иногда проблема кроется не в самом запросе, а в настройках соединения. Давайте проверим настройки драйвера ODBC и системные переменные на сервере, где работает служба 1С.

1. Переменная NLS_LANG

Убедитесь, что в системных переменных окружения (Environment Variables) задана переменная NLS_LANG. Она сообщает клиенту Oracle (которым является сервер 1С), в какой кодировке ожидать данные. Для России значение часто выглядит как:

Важно: после изменения переменной службу сервера 1С необходимо перезапустить.

2. Настройки DSN (Data Source Name)

В настройках источника ODBC для драйвера Oracle обратите внимание на вкладку Workarounds или Options. Следующие параметры могут помочь:

Резюме

Проблема "Двоичные данные ломают строки" — классический пример конфликта на уровне драйверов баз данных при работе через прослойку внешних источников. Хотя манипуляции с порядком полей и настройками драйвера могут помочь, мы рекомендуем использовать архитектурный подход:

Разделяйте данные. Не выводите поля типа ДвоичныеДанные в списки и формы по умолчанию. Загружайте тяжелые данные только в тот момент, когда они действительно нужны пользователю (нажатие кнопки «Скачать» или «Сохранить»), используя для этого отдельный точечный запрос.

← На главную