При интеграции 1С с внешними базами данных на базе MS SQL Server часто возникает задача не только передать данные в хранимую процедуру (ХП), но и получить обратный отклик. Это может быть статус выполнения, сообщение об ошибке или сформированный идентификатор (GUID). Рассмотрим по шагам, как реализовать этот механизм, используя технологию ADO (ActiveX Data Objects).
Представим типичную ситуацию: в конфигурации 1С есть обработка, которая подключается к SQL-серверу и вызывает процедуру docProvercaSclada. Процедура принимает ряд параметров и должна вернуть текстовое сообщение в переменную @msg. Проблема заключается в том, что при простом вызове через Connection.Execute результат работы процедуры остается внутри SQL-сервера, и 1С его «не видит».
Разберем три основных способа решения этой задачи: от самого простого до профессионального.
Это самый быстрый способ, если у вас есть возможность вносить изменения в текст хранимой процедуры на стороне SQL. Суть заключается в том, чтобы в конце тела процедуры выполнить команду SELECT, которая вернет значение выходного параметра как обычную таблицу.
Проанализируем изменения в SQL-процедуре:
ALTER PROCEDURE [dbo].[docProvercaSclada]
@doc UNIQUEIDENTIFIER OUTPUT,
@msg VARCHAR(500) OUTPUT
AS
BEGIN
SET NOCOUNT ON; -- Важно для ADO!
-- Логика процедуры
IF @doc IS NULL SET @msg = 'Ошибка: Документ не найден';
-- В самом конце возвращаем значение
SELECT @msg AS ReturnMessage;
END
Теперь рассмотрим, как получить это значение в 1С. Вместо обычного выполнения команды мы используем объект RecordSet:
ТекстКоманды = "EXEC docProvercaSclada @doc='...', @msg=''";
Выборка = Новый COMОбъект("ADODB.RecordSet");
Выборка = Соединение.Execute(ТекстКоманды);
Если НЕ Выборка.EOF() Тогда
СообщениеИзSQL = Выборка.Fields("ReturnMessage").Value;
Сообщить("Ответ от сервера: " + СообщениеИзSQL);
КонецЕсли;
Если политикой безопасности запрещено менять саму хранимую процедуру, мы можем обернуть её вызов в небольшой скрипт прямо в тексте запроса из 1С. Мы объявим переменную в SQL, передадим её в процедуру как OUTPUT, а затем сразу выберем её значение.
Разберем пример кода для этого случая:
// Формируем текст запроса, который выполнит SQL
ТекстИнструкции =
"DECLARE @res_msg VARCHAR(500); " +
"EXEC docProvercaSclada @doc=NULL, @msg=@res_msg OUTPUT; " +
"SELECT @res_msg AS msg;";
Выборка = Соединение.Execute(ТекстИнструкции);
Если НЕ Выборка.EOF() Тогда
Результат = Выборка.Fields("msg").Value;
Сообщить("Результат выполнения: " + Результат);
КонецЕсли;
Этот метод удобен тем, что не требует сложной настройки объектов ADO, но он уязвим к ошибкам форматирования строк (например, если в строковых параметрах встретятся кавычки).
Самым надежным и правильным способом является работа с объектом Command и его коллекцией Parameters. Это позволяет избежать проблем с типами данных (особенно с GUID и DateTime) и защищает от SQL-инъекций.
Рассмотрим по шагам реализацию этого метода:
Command и связываем его с активным соединением.adCmdStoredProc (значение 4).CreateParameter.
Команда = Новый COMОбъект("ADODB.Command");
Команда.ActiveConnection = Соединение;
Команда.CommandText = "docProvercaSclada";
Команда.CommandType = 4; // adCmdStoredProc
// Создаем входной параметр (GUID)
// 72 - adGuid, 1 - adParamInput
Парам1 = Команда.CreateParameter("@doc", 72, 1, , GUIDДокумента);
Команда.Parameters.Append(Парам1);
// Создаем выходной параметр для сообщения
// 200 - adVarChar, 2 - adParamOutput, 500 - длина
ПарамMsg = Команда.CreateParameter("@msg", 200, 2, 500);
Команда.Parameters.Append(ПарамMsg);
// Выполняем
Команда.Execute();
// Получаем значение напрямую из коллекции параметров
ОтветСервера = Команда.Parameters("@msg").Value;
Сообщить(ОтветСервера);
При реализации обмена данными важно учитывать технические особенности взаимодействия 1С и MS SQL Server, которые часто становятся причиной трудноотловимых ошибок.
1. Настройка SET NOCOUNT ON
Проанализируем работу SQL Server: по умолчанию после каждой операции INSERT или UPDATE сервер отправляет сообщение о количестве затронутых строк. ADO воспринимает эти сообщения как технические наборы данных. В итоге, когда вы пытаетесь прочитать RecordSet, вы получаете ошибку "Объект закрыт". Всегда добавляйте SET NOCOUNT ON; в начало вашей хранимой процедуры.
2. Работа с типом uniqueidentifier (GUID)
Выясним причину частых ошибок при передаче GUID. Если вы формируете запрос строкой, 1С передает GUID как "bb0343a4-0466-4890-a544-67258428a99d". В SQL-запросе эта строка обязательно должна быть в одинарных кавычках. Если же вы используете коллекцию Parameters, используйте тип adGuid (код 72), чтобы драйвер сам позаботился о бинарном представлении.
3. Обработка нескольких наборов записей
Если ваша процедура делает несколько вызовов SELECT, 1С увидит только первый. Чтобы переключиться на следующий набор (например, если в конце процедуры вы возвращаете статус), используйте метод NextRecordset() объекта Recordset.
4. Управление тайм-аутами
Посмотрим на параметры ConnectionTimeout и CommandTimeout. Для длительных операций на стороне SQL (например, проверка склада за большой период) стандартных 30 секунд может не хватить. Устанавливайте CommandTimeout в значение 0 (бесконечно) или достаточно большое число (600 и более), чтобы избежать аварийного завершения сеанса 1С.
5. Выбор провайдера
Для современных версий SQL Server (2012+) рекомендуется использовать провайдер MSOLEDBSQL вместо стандартного SQLOLEDB. Он лучше поддерживает современные типы данных и работает стабильнее с кодировками кириллицы.
Используя предложенные методы, вы сможете надежно связать бизнес-логику 1С с мощными инструментами обработки данных на стороне SQL-сервера, обеспечив качественную обратную связь для пользователей.