При организации обмена данными между 1С:Предприятием и внешними базами данных на базе SQL Server программисты часто сталкиваются с проблемой записи строковых полей. Одной из самых распространенных ошибок является наличие в тексте (например, в наименовании контрагента) одинарной кавычки или апострофа ('). Если формировать SQL-запрос путем простой конкатенации строк, этот символ разрывает синтаксическую конструкцию запроса, что приводит к ошибке выполнения.
В данной статье мы подробно разберем, почему возникает эта проблема, и рассмотрим несколько способов ее решения: от простого экранирования символов до профессионального использования параметризованных запросов и встроенных механизмов платформы 1С.
Разберем ситуацию на примере. Типовой SQL-запрос на вставку данных выглядит следующим образом:
INSERT INTO tbl (Name) VALUES ('Наименование')
Текст внутри VALUES заключается в одинарные кавычки. Если в самом наименовании встречается апостроф, например, ООО 'Окей', то результирующий запрос примет вид:
INSERT INTO tbl (Name) VALUES ('ООО 'Окей'')
SQL-сервер интерпретирует вторую кавычку (перед словом Окей) как закрывающую для строки. Все, что идет следом, сервер воспринимает как ошибочную команду, что вызывает прерывание транзакции.
Самый быстрый и простой способ, который часто применяется в простых обработках выгрузки, — это удвоение кавычки. В языке SQL символ одинарной кавычки, идущий дважды (''), воспринимается сервером как часть строки, а не как управляющий символ.
Проанализируем, как это реализовать в коде 1С с помощью функции СтрЗаменить. Мы заменим одну кавычку на две:
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Экранируем кавычки в наименовании
ЧистоеНаименование = СтрЗаменить(Выборка.Наименование, "'", "''");
ЧистоеПолноеНаименование = СтрЗаменить(Выборка.НаименованиеПолное, "'", "''");
Command.CommandText = "INSERT INTO [db_1c].[dbo].[tblКонтрагенты]
|([КодКонтрагента],[Наименование],[ПолноеНаименование])
|VALUES ('" + Выборка.Код + "', '" + ЧистоеНаименование + "', '" + ЧистоеПолноеНаименование + "')";
Command.Execute();
КонецЦикла;
Важно понимать, что этот метод исправляет ошибку синтаксиса, но не решает других проблем, таких как корректная передача Unicode (кириллицы) или защита от SQL-инъекций.
Рассмотрим более надежный и профессиональный подход. Вместо того чтобы собирать текст запроса "плюсиками", мы создадим шаблон запроса с параметрами-заполнителями (знаками вопроса), а значения передадим отдельно. В этом случае драйвер ADO самостоятельно позаботится об экранировании любых спецсимволов.
Выясним преимущества этого метода:
Посмотрим на пример реализации с использованием объекта Parameter:
Command = Новый COMОбъект("ADODB.Command");
Command.ActiveConnection = Connection;
// Используем знаки вопроса как заглушки для параметров
Command.CommandText = "INSERT INTO tblКонтрагенты (Код, Наименование) VALUES (?, ?)";
// Создаем параметры: имя, тип, направление, размер, значение
// 202 - это adVarWChar (строка Unicode)
// 1 - это adParamInput (входной параметр)
ParamКод = Command.CreateParameter("Код", 202, 1, 10, Выборка.Код);
Command.Parameters.Append(ParamКод);
ParamName = Command.CreateParameter("Наименование", 202, 1, 150, Выборка.Наименование);
Command.Parameters.Append(ParamName);
// В цикле достаточно просто менять значения параметров и выполнять команду
Пока Выборка.Следующий() Цикл
Command.Parameters("Код").Value = Выборка.Код;
Command.Parameters("Наименование").Value = Выборка.Наименование;
Command.Execute();
КонецЦикла;
Проанализируем еще одну важную деталь. Если в SQL-сервере поле имеет тип nvarchar, а вы используете ручную сборку строки запроса (как в Решении 1), вы можете столкнуться с тем, что вместо русских букв запишутся знаки вопроса (???). Чтобы этого избежать, перед строковой константой в SQL-запросе нужно ставить префикс N.
Пример правильной сборки строки:
"VALUES (N'" + ЧистоеНаименование + "')"
Символ N указывает SQL-серверу, что далее следует строка в кодировке National (Unicode), что крайне важно для корректной работы с кириллицей в 1С.
Если вы выгружаете большое количество записей (например, 5000 контрагентов), выполнение каждого метода Execute() по отдельности будет работать крайне медленно. Каждая такая команда инициирует отдельную транзакцию на дисковой подсистеме сервера.
Разберем, как ускорить процесс в десятки раз с помощью транзакций COM-объекта Connection:
Connection.BeginTrans(); // Открываем одну общую транзакцию
Попытка
Пока Выборка.Следующий() Цикл
// Код записи через Command.Execute
КонецЦикла;
Connection.CommitTrans(); // Фиксируем все изменения разом
Исключение
Connection.RollbackTrans(); // Отменяем все, если произошла ошибка
Сообщить("Ошибка при выгрузке: " + ОписаниеОшибки());
КонецПопытки;
Если вы работаете на платформе 1С версии 8.2.14 и выше, мы рекомендуем рассмотреть механизм Внешних источников данных. Это позволяет подключить таблицу SQL прямо в дерево конфигурации 1С. В этом случае работа с SQL-таблицей будет выглядеть так же, как работа с обычным справочником 1С:
ВнешниеИсточникиДанных.ИмяИсточника.Таблицы.ИмяТаблицы.СоздатьМенеджерЗаписи().Это избавляет от необходимости писать низкоуровневый код на ADO и значительно повышает надежность системы.
При записи данных обязательно учитывайте длину полей в SQL. Если в 1С наименование составляет 200 символов, а в SQL поле определено как nvarchar(150), вы получите ошибку "String or binary data would be truncated". Рекомендуется использовать функцию Лев() для обрезки данных перед передачей в SQL.
Также помните про пустые даты. Пустая дата 1С (01.01.0001) не поддерживается SQL-сервером (минимальная дата там обычно 1753 год). Для таких случаев в параметризованных запросах следует передавать значение Null.