При интеграции систем часто возникает ситуация, когда необходимо передавать строковые данные на различных национальных языках (например, грузинском, армянском или кириллице) из базы данных 1С в СУБД MS SQL. Однако разработчики, использующие механизм Внешних источников данных (ВИД) на старых версиях платформы (вплоть до 1С:Предприятие 8.3.12, включая популярную сборку 8.3.9.2170), сталкиваются с неприятным поведением: вместо красивых грузинских букв вроде საკისარი в базу данных MS SQL записываются исключительно знаки вопроса (?????).
В этой статье мы подробно проанализируем, почему возникает эта проблема, разберем глубинную причину бага платформы 1С и рассмотрим несколько надежных практических способов обхода этого ограничения без необходимости обновления платформы.
Первое интуитивное действие любого SQL-разработчика при появлении знаков вопроса — проверить типы данных в приемнике и убедиться, что строковые литералы передаются с префиксом Юникода N. Однако в случае с Внешними источниками данных 1С классическая конструкция вида ProductName = "N'" + ProductName + "'" не приносит результата.
Проблема кроется в архитектуре взаимодействия платформы 1С с драйверами ODBC. При инициализации подключения к MS SQL платформа запрашивает метаданные СУБД, включая кодировку по умолчанию (Collation) всей базы данных. Если целевая база данных MS SQL имеет Collation семейства SQL_Latin1_General_* (что крайне часто встречается в зарубежных облачных инстансах, таких как Microsoft Azure, или при дефолтной установке английской версии SQL Server), ядро 1С делает ошибочный вывод:
«Внешняя база данных не поддерживает Unicode-символы в параметрах запросов».
В результате срабатывает внутренний алгоритм платформы 1С:
CP1252 / Windows Western).CP1252. В процессе необратимой конвертации они превращаются в байт 0x3F (символ знака вопроса ?).????? и честно записывает ее в поле типа NVARCHAR. На стороне SQL-сервера мы видим знаки вопроса, хотя сам SQL-сервер настроен правильно.Давайте разберем по шагам, как можно обойти эту проблему в рамках платформы 8.3.9.2170.
---
Если у вас есть административный доступ к серверу MS SQL и изменение настроек базы данных не нарушит работу других связанных систем, это самый простой и чистый способ решить проблему раз и навсегда.
Платформа 1С перестанет принудительно урезать строки до CP1252, если изменить Collation базы данных на тот, который поддерживает кириллицу или национальные кодировки (например, Cyrillic_General_CI_AS или специализированный грузинский Georgian_Modern_Sort_CI_AS). При подключении к такой базе 1С поймет, что СУБД способна работать с расширенным набором символов.
Для изменения Collation выполните следующий T-SQL скрипт на стороне SQL-сервера (предварительно переведя базу в режим single user):
USE master;
ALTER DATABASE [ИмяВашейБазы] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE [ИмяВашейБазы] COLLATE Cyrillic_General_CI_AS;
ALTER DATABASE [ИмяВашейБазы] SET MULTI_USER;
После изменения Collation пересоздайте подключение во Внешних источниках данных 1С. Строки начнут передаваться корректно без каких-либо изменений в программном коде.
---
Если вы выполняете запись во внешнюю таблицу с помощью прямых SQL-запросов через конструкцию Новый Запрос (или динамические SQL-вызовы), платформа выполняет текстовую трансляцию запроса, где и происходит сбой кодировки.
Однако если использовать встроенные объектные методы работы с таблицами Внешних источников данных, платформа использует типизированные параметры. Рассмотрим пример объектной записи данных, который часто помогает избежать искажения кодировки:
// Получаем менеджер таблицы внешнего источника данных
НаборЗаписей = ВнешниеИсточникиДанных.ИмяВашегоИсточника.Таблицы.ИмяТаблицы.СоздатьНаборЗаписей();
// Создаем новую строку
НоваяСтрока = НаборЗаписей.Добавить();
НоваяСтрока.ProductName = "საკისარი"; // Грузинский текст "Подшипник"
НоваяСтрока.ProductCode = "SKF-102";
// Записываем данные в базу SQL
Попытка
НаборЗаписей.Записать();
Сообщить("Данные успешно записаны!");
Исключение
Сообщить("Ошибка при записи: " + ОписаниеОшибки());
КонецПопытки;
При объектном подходе платформа 1С генерирует параметризованный запрос, что минимизирует риски искажения строковых литералов парсером 1С.
---
Что делать, если база данных MS SQL вам не принадлежит (вы не можете менять Collation), а объектный способ записи по каким-то причинам недоступен или выдает ту же ошибку? В этом случае можно использовать красивый архитектурный обходной путь: передавать данные в кодировке Base64.
Суть метода заключается в том, что строка Base64 состоит исключительно из латинских букв и цифр (символы ASCII). Она гарантированно пройдет через любые ограничения конвертации 1С без искажений. На стороне MS SQL мы декодируем данные обратно в исходный Юникод.
Шаг 1. Кодирование строки на стороне 1С:
Напишем функцию для надежного преобразования строки в формат Base64 с явным указанием кодировки UTF-8:
Функция ПревратитьСтрокуВBase64(ИсходнаяСтрока) Экспорт
// Создаем временный файл для записи текста в UTF-8
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("txt");
ЗаписьТекста = Новый ЗаписьТекста(ИмяВременногоФайла, КодировкаТекста.UTF8);
ЗаписьТекста.ЗаписатьСтроку(ИсходнаяСтрока);
ЗаписьТекста.Закрыть();
// Читаем файл как двоичные данные
ДвоичныеДанные = Новый ДвоичныеДанные(ИмяВременногоФайла);
СтрокаBase64 = ПолучитьBase64СтрокуИзДвоичныхДанных(ДвоичныеДанные);
// Удаляем временный файл
УдалитьФайлы(ИмяВременногоФайла);
Возврат СтрокаBase64;
КонецФункции
Шаг 2. Декодирование на стороне MS SQL:
На стороне SQL-сервера мы можем восстановить исходную строку NVARCHAR, используя встроенный XML-декодер СУБД. Ниже представлен пример того, как это сделать в T-SQL:
-- Предположим, мы передали строку "საკისარი" в Base64: "4YOh4YOt4YOY4YOh4YOh4YOt4YOY"
DECLARE @Base64Encoded VARCHAR(MAX) = '4YOh4YOt4YOY4YOh4YOh4YOt4YOY';
DECLARE @DecodedText NVARCHAR(MAX);
-- Декодируем бинарные данные обратно в строку UTF-8
SET @DecodedText = CAST(
CAST('' AS XML).value('xs:base64Binary(sql:variable("@Base64Encoded"))', 'VARBINARY(MAX)')
AS NVARCHAR(MAX)
);
SELECT @DecodedText; -- Вернет: საკისარი
Этот декодирующий функционал вы можете обернуть в SQL-триггер INSTEAD OF INSERT на целевой таблице или вызывать соответствующую хранимую процедуру. Этот способ полностью защищен от любых багов локализации платформы 1С.
---
Автор обсуждения на форуме отметил, что при использовании ADODB через COM-соединение проблем с кодировкой не возникало. Однако COM-объекты могут работать медленнее при построчной обработке больших массивов данных. Чтобы решить проблему производительности при работе с COM, необходимо использовать параметризованные запросы.
Когда вы используете параметры в ADODB, вы явно указываете тип данных Юникод (тип 202 — adVarWChar). В этом случае драйвер OLE DB или ODBC передает данные напрямую в обход парсера строковых литералов 1С.
Проанализируем пример быстрой пакетной вставки данных с использованием параметров через ADODB:
Соединение = Новый COMОбъект("ADODB.Connection");
Команда = Новый COMОбъект("ADODB.Command");
// Строка подключения с использованием современного драйвера
СтрокаСоединения = "Provider=MSOLEDBSQL;Data Source=ИмяСервера;Initial Catalog=ИмяБазы;Integrated Security=SSPI;";
Соединение.Open(СтрокаСоединения);
Команда.ActiveConnection = Соединение;
Команда.CommandTimeout = 30;
// Подготавливаем параметризованный SQL-запрос
Команда.CommandText = "INSERT INTO Products (ProductCode, ProductName) VALUES (?, ?)";
// Создаем параметры (тип 202 соответствует adVarWChar для корректного Юникода)
ПараметрКод = Команда.CreateParameter("ProductCode", 202, 1, 50, "SKF-102");
Команда.Parameters.Append(ПараметрКод);
ПараметрИмя = Команда.CreateParameter("ProductName", 202, 1, 150, "საკისარი");
Команда.Parameters.Append(ПараметрИмя);
// Начинаем транзакцию для ускорения работы с большими объемами
Соединение.BeginTrans();
Попытка
// Выполняем команду
Команда.Execute();
Соединение.CommitTrans();
Сообщить("Строка успешно добавлена через ADODB!");
Исключение
Соединение.RollbackTrans();
Сообщить("Ошибка при записи: " + ОписаниеОшибки());
КонецПопытки;
Соединение.Close();
Применение явных транзакций (BeginTrans / CommitTrans) при работе через COM-соединение увеличивает скорость вставки данных в десятки раз, что практически нивелирует разницу в скорости с Внешними источниками данных.
---
В некоторых случаях искажение кодировки можно предотвратить тонкой настройкой параметров ODBC прямо в строке подключения Внешнего источника данных:
{SQL Server} пропишите использование {ODBC Driver 17 for SQL Server} или {ODBC Driver 18 for SQL Server}.AutoTranslate=No (или Translate=False).
Пример строки подключения для ВИД:
Driver={ODBC Driver 17 for SQL Server};Server=MyServer;Database=MyDatabase;Uid=myUsername;Pwd=myPassword;AutoTranslate=No;
Параметр AutoTranslate=No запрещает драйверу выполнять неявную конвертацию Юникод-символов в системную кодовую страницу Windows (которая на русскоязычных серверах по умолчанию является CP1251, а на англоязычных — CP1252), что позволяет сохранить исходные коды грузинских символов.
---
Мы подробно разобрали системную проблему передачи не-латинских символов через Внешние источники данных в старых версиях 1С. Выяснили, что виновником является некорректная обработка кодовых страниц ядром платформы при взаимодействии со специфическими Collation баз данных MS SQL.
Для гарантированного решения проблемы рекомендуется:
Cyrillic_General_CI_AS.adVarWChar) внутри SQL-транзакций.