При интеграции 1С:Предприятия со сторонними базами данных Microsoft SQL Server с использованием технологии COM часто возникает задача вызова хранимой процедуры. Особую сложность вызывает сценарий, когда процедура должна одновременно возвращать как результирующий набор строк (Recordset через оператор SELECT), так и исходящие параметры (OUTPUT). Разберем, как правильно инициализировать параметры команды, выполнить вызов и корректно прочитать все возвращаемые данные.
Рассмотрим структуру вызова хранимой процедуры с использованием объекта ADODB.Command. Для корректной передачи параметров необходимо явно определить их тип и направление передачи с помощью метода CreateParameter, после чего добавить их в коллекцию Parameters.
Разберем пример создания подключения и выполнения хранимой процедуры, возвращающей выходной параметр:
COMОбъектSQL = Новый COMОбъект("ADODB.Connection");
COMОбъектSQL.ConnectionTimeOut = 30;
COMОбъектSQL.CommandTimeOut = 60;
COMОбъектSQL.ConnectionString = "Driver={SQL Server}; SERVER=MyServer; Database=MyBase; Uid=sa; Pwd=secret;";
COMОбъектSQL.Open();
Command = Новый COMОбъект("ADODB.Command");
Command.CommandTimeout = 100;
Command.ActiveConnection = COMОбъектSQL;
Command.CommandType = 4; // adCmdStoredProc (хранимая процедура)
Command.CommandText = "VladTest";
// Создание исходящего строкового параметра (adVarChar = 200, adParamOutput = 2, длина = 20)
Param = Command.CreateParameter("@p1", 200, 2, 20);
Command.Parameters.Append(Param);
// Выполнение команды без сохранения выборки
Command.Execute();
// Чтение значения возвращенного параметра
РезультатПараметра = Command.Parameters("@p1").Value;
Сообщить("Значение исходящего параметра: " + Строка(РезультатПараметра));
В этом примере процедура выполняется корректно, и значение параметра становится доступным в свойстве Value сразу после вызова метода Execute().
Проанализируем ситуацию, когда хранимая процедура возвращает не только выходной параметр, но и выборку строк:
ALTER PROCEDURE [dbo].[VladTest]
@p1 varchar(20) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT @p1 = 'Результат параметра';
SELECT 'Строка выборки 1' AS Поле1;
END
Если в коде 1С мы перехватываем результат выполнения в объект набора записей через RS = Command.Execute();, то попытка сразу прочитать значение Command.Parameters("@p1").Value вернет Неопределено или Null.
Выясним причину такого поведения. Согласно протоколу взаимодействия TDS (Tabular Data Stream) между клиентом OLE DB/ADO и MS SQL Server, сервер передает данные выходных параметров (OUTPUT) и код возврата (RETURN) в самом конце потока данных — строго после того, как переданы все строки всех результирующих наборов. Пока объект Recordset остается открытым и ассоциированным с активным соединением, ADO блокирует заполнение значений в коллекции Parameters.
Чтобы успешно получить и результирующий набор записей, и значения параметров OUTPUT, необходимо соблюдать строгую последовательность действий. Разберем по шагам правильный алгоритм:
ADODB.Command, добавить все входные и выходные параметры через CreateParameter и метод Append.RS = Command.Execute();.ТаблицаЗначений или массив структур).RS.Close();. Именно в этот момент ADO завершает чтение TDS-потока и наполняет значения выходных параметров.Command.Parameters("@ИмяПараметра").Value.Посмотрим на полный практический пример реализации этого алгоритма в коде 1С:
// 1. Инициализация команды
Command = Новый COMОбъект("ADODB.Command");
Command.CommandTimeout = 100;
Command.ActiveConnection = COMОбъектSQL;
Command.CommandType = 4; // adCmdStoredProc
Command.CommandText = "VladTest";
// Добавление выходного параметра
ParamOut = Command.CreateParameter("@p1", 200, 2, 20);
Command.Parameters.Append(ParamOut);
// 2. Выполнение и получение Recordset
RS = Command.Execute();
// 3. Обход строк набора данных
ТаблицаРезультатов = Новый ТаблицаЗначений;
ТаблицаРезультатов.Колонки.Добавить("Поле1");
Пока RS.EOF() = 0 Цикл
НоваяСтрока = ТаблицаРезультатов.Добавить();
НоваяСтрока.Поле1 = RS.Fields(0).Value;
RS.MoveNext();
КонецЦикла;
// 4. Явное закрытие набора записей для освобождения потока данных
RS.Close();
// 5. Чтение исходящего параметра
ВыходноеЗначение = Command.Parameters("@p1").Value;
Сообщить("Прочитано из выборки строк: " + Строка(ТаблицаРезультатов.Количество()));
Сообщить("Прочитан выходной параметр: " + Строка(ВыходноеЗначение));
Рассмотрим дополнительные технические нюансы, с которыми можно столкнуться при использовании ADODB в 1С:
Execute() открывается однонаправленный серверный курсор (adOpenForwardOnly). У такого курсора свойство RecordCount возвращает -1 или вызывает ошибку. Для обхода данных следует использовать цикл Пока RS.EOF() = 0 Цикл либо предварительно переключить курсор на клиентский, установив свойство COMОбъектSQL.CursorLocation = 3 (adUseClient).INSERT, UPDATE, DELETE) и в ее начале не указана директива SET NOCOUNT ON, SQL Server отправляет служебные сообщения о количестве затронутых строк. ADO воспринимает их как промежуточные закрытые наборы записей, что может привести к пустой выборке. Если директиву изменить нельзя, необходимо перебирать наборы методом RS = RS.NextRecordset(); до получения основного набора строк.Parameters по умолчанию может резервировать нулевой элемент для возвращаемого значения процедуры (RETURN). При позиционном обращении по индексам (0, 1, 2) следует помнить, что пользовательские параметры могут начинаться с индекса 1, поэтому безопаснее обращаться к ним по именам: Command.Parameters("@p1").Value.Следует учитывать системные ограничения:
COMОбъект("ADODB.Command") возможно только на платформе под управлением Windows. Если сервер 1С работает под управлением Linux, вызов COM-объектов вызовет исключение.В качестве кроссплатформенной альтернативы рекомендуется использовать механизм платформы «Внешние источники данных», который работает через ODBC-драйверы без прямого использования COM-объектов.