При интеграции 1С с внешними базами данных SQL Server через механизм ADO (ActiveX Data Objects) программисты часто сталкиваются с трудностями при вызове пользовательских функций (UDF). Основные проблемы обычно связаны либо с ошибкой Invalid object name, либо с тем, что возвращаемый набор записей (Recordset) кажется пустым или не содержит нужных полей. В этой статье мы подробно разберем, как правильно составить запрос, передать параметры и получить результат.
Прежде чем приступать к написанию кода, проанализируем, почему возникают ошибки в разных вариантах написания CommandText. В SQL Server существует два основных типа функций, и синтаксис их вызова в SELECT принципиально различается:
SELECT dbo.ИмяФункции(параметры).SELECT * FROM dbo.ИмяФункции(параметры).Если вы пытаетесь вызвать скалярную функцию через SELECT * FROM..., SQL Server выдаст ошибку "Invalid object name", так как он ожидает увидеть таблицу или представление, а получает скалярный объект. Если же вы вызываете скалярную функцию правильно (через SELECT без FROM), но не указываете псевдоним (alias), то в полученном Recordset поле будет называться либо по имени функции, либо вовсе не будет иметь имени, что приведет к ошибке в 1С при попытке обращения RS.Fields("ИмяПоля").Value.
Рассмотрим пример кода, который часто используется для выполнения таких задач. Разберем его по шагам, чтобы понять, где могут скрываться ошибки.
Соединение = SQLServer.Подключиться();
Command = Новый COMОбъект("ADODB.Command");
RS = Новый COMОбъект("ADODB.RecordSet");
Command.ActiveConnection = Соединение;
Command.CommandType = 1; // adCmdText
Command.NamedParameters = True;
// Здесь кроется проблема выбора синтаксиса:
Command.CommandText = "SELECT [dbo].[ProductID](?, ?) AS prd_ID";
// Настройка параметров
Парам1 = Command.CreateParameter("@Barcode", 202, 1, 40, "22663484730011");
Command.Parameters.Append(Парам1);
Парам2 = Command.CreateParameter("@DepositorID", 3, 1, , 32);
Command.Parameters.Append(Парам2);
RS = Command.Execute();
Пока НЕ RS.EOF() Цикл
// Обращаемся по псевдониму, который указали в SELECT
prd_ID = RS.Fields("prd_ID").Value;
RS.MoveNext();
КонецЦикла;
Если структура запроса простая и данные не требуют сложной типизации, можно вставить значения параметров прямо в текст запроса. Рассмотрим, как это сделать, чтобы избежать лишней сложности с объектами ADODB.Parameter. Выясним причину популярности этого метода: он нагляден и позволяет быстро проверить работу функции в SQL Server Management Studio перед переносом в 1С.
Пример вызова скалярной функции с литералами:
ТекстЗапроса = "SELECT [dbo].[ProductID]('" + Штрихкод + "', " + Идентификатор + ") AS prd_ID";
Command.CommandText = ТекстЗапроса;
RS = Command.Execute();
Важный нюанс: при использовании этого метода необходимо самостоятельно следить за экранированием кавычек в строковых параметрах, чтобы не допустить SQL-инъекций или синтаксических ошибок.
Проанализируем более профессиональный подход — использование знаков вопроса (?) в качестве заглушек (placeholders). Это позволяет драйверу SQL Server ODBC или OLE DB самостоятельно позаботиться о типах данных.
Когда мы выполняем SELECT [dbo].[ProductID](?, ?), SQL Server возвращает колонку без заданного имени. Чтобы 1С могла обратиться к полю по имени, обязательно используйте конструкцию AS ИмяПоля.
Разберем настройки параметров подробнее:
202 (adVarWChar) — это соответствует NVARCHAR в SQL. Тип 200 (adVarChar) подходит для обычного VARCHAR.1 (adParamInput) означает входной параметр.3 — adInteger) это поле можно оставить пустым или 0.Стоит отметить, что свойство Command.NamedParameters = True не всегда работает так, как ожидается. Многие драйверы (особенно старые версии MSDASQL) игнорируют имена параметров (типа @Barcode) и просто сопоставляют их по порядку следования знаков вопроса в тексте запроса. Поэтому крайне важно добавлять параметры через Command.Parameters.Append ровно в том порядке, в котором они перечислены в функции SQL.
Если ваша функция возвращает таблицу (Table-Valued Function), то синтаксис SELECT * FROM будет верным. В этом случае псевдонимы полей обычно уже определены внутри самой функции, и вы можете обращаться к ним напрямую.
// Пример для табличной функции
Command.CommandText = "SELECT prd_ID, prd_Name FROM [dbo].[GetProductData](?, ?)";
// ... добавление параметров ...
RS = Command.Execute();
Если НЕ RS.EOF() Тогда
Имя = RS.Fields("prd_Name").Value;
КонецЕсли;
Рассмотрев все нюансы, мы можем сформулировать основные правила успешного вызова SQL-функций из 1С:
SELECT, если табличная — SELECT FROM.AS MyFieldName для скалярных результатов, чтобы Recordset вернул именованное поле.Parameters в строгом соответствии с их порядком в SQL-функции.202 для юникод-строк и 3 для целых чисел, чтобы избежать ошибок неявного преобразования типов на стороне сервера.Следуя этим правилам, вы сможете избежать многочасовых поисков ошибок и обеспечить стабильную работу интеграции между 1С и SQL Server.