2
Голосовать

Возврат результата из хранимой процедуры

Создано:

Здравствуйте!

Проблема такова: имеется хранимая процедура, в неё передаю параметры, она возвращает 1 или 0.
В SQL студио работает на ура! В TS CRM x25 3.3.0.42 (MS SQL 2005) - не могу получить результат хранимки!

var Parameters = CreateSPParameters();
CreateSPParameter(Parameters, 'InvoiceID', pdtGUID, InvoiceID);
CreateSPParameter(Parameters, 'StoreID', pdtGUID, StoreID);
CreateSPParameter(Parameters, 'isEnought', pdtInteger, 0);

var SQLText = 'exec dbo.tsp_IsEnoughtOfferingInStore :InvoiceID, :StoreID, :isEnought OUTPUT';
Connector.DBEngine.ExecuteCustomSQL(SQLText, Parameters);

var IsEnought = Parameters.ItemsByName('isEnought').ValAsInt;

параметр 'isEnought' постоянно 0

Смотрел: http://community.terrasoft.ua/blogs/2171 и http://community.terrasoft.ua/node/1852

Буду благодарен за любую помощь!

Комментарии

Alexandr Kravchuk

Видимо нужно указать Parameters.ItemsByName('isEnought').ParamType = 1.

Так как у IParameter свойство ParamType: InputOutputParamTypeEnum по-умолчанию имеет значение 0, т.е. входящий:

enum _InputOutputParamTypeEnum {
                ioptInput = 0,
                ioptOutput = 1,
                ioptInputOutput = 2
        } InputOutputParamTypeEnum;

Виталий Ковалишин

Большое спасибо, Александр!!!

Действительно, пропустил указать тип параметра.
Теперь работает:

var Parameters = CreateSPParameters();
CreateSPParameter(Parameters, 'InvoiceID', pdtGUID, InvoiceID);
CreateSPParameter(Parameters, 'StoreID', pdtGUID, StoreID);
CreateSPParameter(Parameters, 'isEnought', pdtInteger, 0);
Parameters.ItemsByName('isEnought').ParamType = 1; // It!

var SQLText = 'exec dbo.tsp_IsEnoughtOfferingInStore :InvoiceID, :StoreID, :isEnought OUTPUT';
Connector.DBEngine.ExecuteCustomSQL(SQLText, Parameters);

var IsEnought = Parameters.ItemsByName('isEnought').ValAsInt;

--
Cogito, ergo sum

Виталий Ковалишин

Рано тему закрывать! Новая проблема :)

При тестирование на обычной (MS SQL) конфигурации выходной параметр заполняется, но, при тестирование через WebServices - параметр НЕ заполняется!

В чем фишка?

--
Cogito, ergo sum

Underscore a.k.a. _

Думаю что фишка в ядре. Скорее всего вебсервисы просто не учитывают тот факт что параметр может возвращаться. Может как-то переделать на использование запроса с кастомной колонкой?

Виталий Ковалишин

Разработчики пообещали сделать сборку 3.3.0.51, где будет исправлена эта ошибка... Ждем-с-с...

--
Cogito, ergo sum

Alexandr Kravchuk

Сегодня добавил для Web сервисов возможность возвращать результаты ExecuteCustomSQL через Output параметры. Обращайтесь в поддержку за сборкой 3.3.0.51.

Виталий Ковалишин

Александр, большое спасибо!

--
Cogito, ergo sum

SSV

Я так понимаю, что описанный здесь способ вернуть результат работы хранимой процедуры в версии 3.0.2.244 не предусмотрен.
Как вернуть результат?

Лабьяк Олег Игоревич

Посмотрите здесь и дальше по комментариям - способ возвращения через SelectQuery с использованием CustomSQL-колонок. При этом в конце хранимой процедуры должен быть Select, который возвращает необходимый результат.

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

а можно подробней? не совсем понятен принцип... каким образом получить результат из сервиса SelectQuery? в конце ХП создал select, а как то что он возвращает увязать с SelectQuery?
там еще рекомендуют создать поле (Result), но как его создать, если выбор возможен только из полей таблицы из секции FROM?

Лабьяк Олег Игоревич

Результат Вы получаете в колонки запроса. Например, если в конце ХП Вы выполняете такой запрос:

SELECT Field1, Field2, Field3 FROM tbl_Table
...

то в самом сервисе SelectQuery необходимо создать CustomSQL-колонки с названиями Field1, Field2, Field3. Причём в свойство "Текст SQL" первой из них Вам необходимо вставить вызов ХП:

1 AS [a] INTO #tmp
DROP TABLE #tmp

EXEC ProcedureName :ParamName

/*

А для таблицы блока FROM сервиса запроса необходимо прописать алиас "*/--" (без двойных кавычек). Это необходимо, чтобы закомментировать остальной текст запроса.

Далее, после получения экземпляра этого SelectQuery и установки значений необходимых параметров результат выполнения ХП можно так: var Dataset = SelectQuery.Open();

Поля датасета будут содержать результат выполнения.

Что касается совета Дениса:

"в конце процедуры напишите что-то типа
select 1 as [Result]
а в SelectQuery создайте поле
Result - целого типа"

это частный случай того, что я написал выше (если хранимая процедура не возвращает ничего, "заставляем" её вернуть фиктивный результат в колонку запроса).

SSV пишет:
там еще рекомендуют создать поле (Result), но как его создать, если выбор возможен только из полей таблицы из секции FROM?

Именно поэтому рекомендуется создавать CustomSQL-колонки. В них не обязательно выбирать поля из таблицы блока FROM. Более того, в этом запросе абсолютно все колонки должны быть типа CustomSQL, а таблица желательно такая, которая содержит одну строку (чтобы ХП не выполнялась столько раз, сколько записей в таблице). Поэтому, поскольку таблица tbl_DatabaseInfo всегда должна содержать только одну запись, и мы не собираемся ни выбирать из неё информацию, ни выполнять над ней никаких операций, используется именно эта таблица.

Загрузите сервис из примера, и Вы увидите, что он удовлетворяет всем описанным требованиям.

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

в конце процедуры у меня такой селект:

SELECT @ContractNewID AS Result

в SelectQuery я вставил CustomSQL-поле с именем Result, в поле "Текст SQL" что-нибудь нужно писать? ибо всё вроде бы и работает, но в Result ничего не передается...

Лабьяк Олег Игоревич

Свойство "Текст SQL" первой из колонок необходимо заполнить текстом:

1 AS [a] INTO #tmp
DROP TABLE #tmp

EXEC ProcedureName :ParamName

/*

В Вашем случае колонка единственная - её свойство "Текст SQL" так же необходимо заполнить. Вместо ProcedureName необходимо указать название Вашей процедуры, далее, если необходимо, - входящие параметры.

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

вот вложил созданный сервис. далее текст из скрипта:

SQ_SP = Services.GetNewItemByUSI('sq_ResultSP');
SQ_SP.Parameters.ItemsByName('ContractOldID').Value = Dataset.ValAsStr('ContractID');
SQ_SP.Parameters.ItemsByName('NewAccountID').Value = Dataset.ValAsStr('AccountToID');
SQ_SP.Parameters.ItemsByName('CertificateDate').Value = Dataset.ValAsDateTime('CertificateDate');
var DatasetResult = SQ_SP.Open();
var IDNewContract = DatasetResult.ValAsGUID('Result');

в IDNewContract возвращается пустая строка...:sad:

Прикрепленный файлРазмер
sq_ResultSP.rar 682 байта
Лабьяк Олег Игоревич

Попробуйте запустить профайлер перед выполнением SQ_SP.Open() и посмотреть, какой запрос он возвращает. Потом выполните этот запрос с помощью Query Analyzer или SQL Server Management Studio и посмотрите на результат, а также на значения параметров, которые передаются в процедуру. Возможно, это подскажет дальнейшее направление анализа проблемы.

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

вот что показывает профайлер:

exec sp_executesql N'SELECT
        1 AS [a] INTO #tmp
drop table #tmp

EXEC spChangeLegalPerson_AddNewContract @P1, @P2, @P3
/* AS [Result]
FROM
        [tbl_DatabaseInfo] AS [*/--]'
,N'@P1 varchar(38),@P2 varchar(38),@P3 datetime','{461DA3E2-8F2D-488B-A175-1F17356ECFAD}','{69D74C53-113C-493A-8F1A-7C6733B58FCE}',''2010-11-03 00:00:00:000''

т.е. передается всё что нужно...

Лабьяк Олег Игоревич

А результат выполнения этого запроса в Management Studio есть?

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

хм... вот только что-то непонятно как это передается дата... но ошибок не выдает, всё отрабатывает правильно...

SSV

да, в Result возвращается GUID...
но - в том виде в котором этот запрос был скопирован из профайлера, он не выполняется, если убрать у даты лишние кавычки, то всё проходит на ура...

Лабьяк Олег Игоревич

Попробуйте при установке параметров использовать Values:

SQ_SP = Services.GetNewItemByUSI('sq_ResultSP');
SQ_SP.Parameters.ItemsByName('ContractOldID').Value = Dataset.Values('ContractID');
SQ_SP.Parameters.ItemsByName('NewAccountID').Value = Dataset.Values('AccountToID');
SQ_SP.Parameters.ItemsByName('CertificateDate').Value = Dataset.Values('CertificateDate');
var DatasetResult = SQ_SP.Open();
var IDNewContract = DatasetResult.ValAsGUID('Result');

Если дата будет не в нужном формате, можно в тексте SQL колонки запроса привести её к нужному типу:

1 AS [a] INTO #tmp DROP TABLE #tmp  EXEC spChangeLegalPerson_AddNewContract :ContractOldID, :NewAccountID, cast(:CertificateDate AS datetime) /*

Также мне кажется, что поскольку значения параметров берутся из одного и того же датасета, достаточно будет в хранимку передавать значение поля ID, а остальные значения непосредственно получать в процедуре. Количество запросов от этого не должно измениться (тот же Select, который Вы выполняете для открытия датасета Dataset, будет выполняться в хранимой процедуре).

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

пробовал использовать Values, в профайлере запрос имеет тот же вид, но при этом выполняется без проблем... попытка привести параметр к типу datetime порождает ошибку - что-то о неправильном параметре... результат в поле Result процедура не возвращает... может и такого способа моя версия не поддерживает? а ADO позволяет это сделать?

Лабьяк Олег Игоревич

Да, ADO должен позволять даже в 3.0.2.

Но возможно, быстрее будет переписать хранимую процедуру, как я писал выше:

Лабьяк Олег Игоревич пишет:
поскольку значения параметров берутся из одного и того же датасета, достаточно будет в хранимку передавать значение поля ID, а остальные значения непосредственно получать в процедуре.

Насколько я понимаю, проблема только в параметре типа datetime, а с параметром ID типа uniqueidentifier проблем возникнуть не должно.

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

проблем с datetime нет, проблема с возвратом уникального идентификатора. прийдется формировать его на стороне клиента и передавать в процедуру, а это не есть хорошо...

Лабьяк Олег Игоревич

Можете выложить текст процедуры?

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

ALTER PROCEDURE [dbo].[spChangeLegalPerson_AddNewContract]
(
@ContractOldID uniqueidentifier,
@NewAccountID uniqueidentifier,
@CreateByID uniqueidentifier,
@CertificateDate datetime
)
AS

declare @TableName varchar(128)
declare @FieldName varchar(128)
declare @StrSelectSQL varchar(4096)
declare @StrInsertSQL_Fields varchar(4096)
declare @StrInsertSQL_Values varchar(4096)
declare @ContractNewID uniqueidentifier

BEGIN
SET @ContractNewID = newid()
exec dbo.spCreateInsert 'tbl_Contract', @ContractOldID, @ContractNewID, @NewAccountID, @CertificateDate, @CreateByID, @StrInsertSQL_Fields output
execute (@StrInsertSQL_Fields)

exec dbo.spCreateInsert 'tbl_ContractRight', @ContractOldID, @ContractNewID, @NewAccountID, @CertificateDate, @CreateByID, @StrInsertSQL_Fields output
execute (@StrInsertSQL_Fields)

declare CurTable cursor FOR
        SELECT so.name FROM dbo.sysobjects so
        INNER JOIN INFORMATION_SCHEMA.COLUMNS ic ON so.name = ic.table_name
        WHERE so.xtype = 'U' AND so.name LIKE 'tbl_%' AND ic.column_name = 'ContractID'
        ORDER BY so.name
open CurTable
fetch next FROM CurTable INTO @TableName
while (@@fetch_status = 0)
begin
        print @TableName
        IF (@TableName = 'tbl_working')
                begin
                        fetch next FROM CurTable INTO @TableName
                        continue
                end
        print @StrInsertSQL_Fields
        exec dbo.spCreateInsert @TableName, @ContractOldID, @ContractNewID, @NewAccountID, @CreateByID, @CertificateDate, @CreateByID, @StrInsertSQL_Fields output
        IF (@StrInsertSQL_Fields IS NOT NULL)
        begin
                execute (@StrInsertSQL_Fields)
        end
        fetch next FROM CurTable INTO @TableName
end
close CurTable
deallocate CurTable
SELECT @ContractNewID AS Result
END

Лабьяк Олег Игоревич

Мне кажется, принципиальной разницы не будет, формируется ли значение @ContractNewID внутри процедуры или формируется в конфигурации с помощью Connector.GenGUID() и передаётся в качестве параметра.

Насколько я понял, значение @ContractNewID внутри процедуры генерируется только один раз, и больше нигде не меняется. Я попробовал воспроизвести, используя Ваш запрос и оставив в теле процедуры только строки

SET @ContractNewID = newid()
SELECT @ContractNewID AS Result

Результат возвращался корректно. Проверял на версии 3.0.2.231. Почему не работает у Вас - пока версий нет.

Кстати, заметил, что выложенная Вами процедура требует 4 входящих параметра. Вы учли это в Вашем запросе?

Также попробуйте в тексте процедуры взять Result в квадратные скобки или заменить на другой алиас в процедуре и запросе.

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

да, конечно же все параметры передаются и процедура отрабатывает правильно. попробую скобки и другой алиас...

SSV

Приходится поднимать тему в связи с переходом на SQL2008.

1 AS [a] INTO #tmp
DROP TABLE #tmp

EXEC ProcedureName :ParamName

/*

при попытке выполнить метод Open() появляется ошибка о том, что комментарий должен быть завершен... вероятно побороть это нельзя?
Тогда каким образом можно вернуть значение из ХП в версии 3.0.2.244?

Лабьяк Олег Игоревич

У Вас точно алиас таблицы в блоке FROM такой: */-- ?

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

извиняюсь, не доглядел... СПСБ...
только значение не возвращается всё равно, точнее возвращается NULL, хотя я точно знаю, что должна быть осмысленная строка... если запрос из профайлера выполнить в студии, то значение есть, но оно не попадает в датасет...
ХП:

ALTER PROCEDURE [dbo].[spAccountStatus]
        @IDBalSys integer
AS
BEGIN
        declare @StatusAccount varchar(250)
        execute ('BEGIN SRVC.SERVICES_PKG.GETCLIENTSTATUSCRM(?,?); END;', @IDBalSys, @StatusAccount output) AT DEVELOPER;
        SELECT @StatusAccount
END

скрипт:

var SelectQuery = Services.GetNewItemByUSI('sq_StatusAccount');
SelectQuery.Parameters.ItemsByName('IDBalSys').Value = IDBalSys;
var DatasetResult = SelectQuery.Open();
var StatusAccount = DatasetResult.Values('StatusAccount');

в прицепе файл сервиса...

Прикрепленный файлРазмер
sq_statusaccount.rar 622 байта
Лабьяк Олег Игоревич

Думаю, для решения проблемы необходимо изменить последнюю строку хранимой процедуры следующим образом:

  SELECT @StatusAccount AS [StatusAccount]

Дело в том, что процедура в том виде, в котором она есть сейчас, возвращает результат в колонку с неопределённым именем. А Вам необходимо получить результат из колонки "StatusAccount".

С уважением,

Олег Лабьяк,
инженер-программист,
группа компаний Terrasoft.

SSV

СПСБ!

Шальнева Марина

Здравствуйте!

Есть хранимая процедура, которая возвращает запрос (4 поля).

Подскажите, пожалуйста, каким образом их все вывести?

SSV

Вероятно должно быть так: одно поле должно быть создано, как описано в сообщениях 28 и 29, а остальные должны быть просто созданы, а вот в ХП, в результирующем запросе все 4 поля должны быть описаны, как в сообщении 31, т.е. каждому полю должен соответствовать псевдоним в точности повторяющий название поля в сервисе...

Олейник Дмитрий

Здравствуйте, Марина.

В хранимой процедуре Вам нужно создать четыре исходящих параметра, присвоить им значения результатом запроса, и далее в коде создать четыре исходящих параметра, запустить хранимую процедуру, и считать их значения.

Приблизительный код:

(хранимая процедура)

CREATE PROCEDURE tspTest
@isEnought1 [uniqueidentifier] output,
@isEnought2 [varchar](250) output,
@isEnought3 [varchar](250) output,
@isEnought4 [uniqueidentifier]output
AS
BEGIN
 SET @isEnought1 = (SELECT ID FROM tbl_Account WHERE ID = '{5F982A3D-0AAC-4306-8DBF-5F2327987615}')
 SET @isEnought2 = (SELECT Name FROM tbl_Account WHERE ID = '{5F982A3D-0AAC-4306-8DBF-5F2327987615}')
 SET @isEnought3 = (SELECT OfficialAccountName FROM tbl_Account WHERE ID = '{5F982A3D-0AAC-4306-8DBF-5F2327987615}')
 SET @isEnought4 = (SELECT OwnerID FROM tbl_Account WHERE ID = '{5F982A3D-0AAC-4306-8DBF-5F2327987615}')
END
GO

(код скрипта)

function Main()
{

var Parameters = CreateSPParameters();
 
CreateSPParameter(Parameters, 'isEnought1', pdtGUID, 0);
CreateSPParameter(Parameters, 'isEnought2', pdtString, 0);
CreateSPParameter(Parameters, 'isEnought3', pdtString, 0);
CreateSPParameter(Parameters, 'isEnought4', pdtGUID, 0);

Parameters.ItemsByName('isEnought1').ParamType = 1; // It!
Parameters.ItemsByName('isEnought2').ParamType = 1; // It!
Parameters.ItemsByName('isEnought3').ParamType = 1; // It!
Parameters.ItemsByName('isEnought4').ParamType = 1; // It!

var SQLText = 'exec dbo.tspTest3 :isEnought1 OUTPUT, :isEnought2 OUTPUT, :isEnought3 OUTPUT, :isEnought4 OUTPUT';
Connector.DBEngine.ExecuteCustomSQL(SQLText, Parameters);

var IsEnought1 = Parameters.ItemsByName('isEnought1').Value;
var IsEnought2 = Parameters.ItemsByName('isEnought2').Value;
var IsEnought3 = Parameters.ItemsByName('isEnought3').Value;
var IsEnought4 = Parameters.ItemsByName('isEnought4').Value;   
}

С уважением,
Олейник Дмитрий
Эксперт 3-й линии поддержки

Шальнева Марина

Спасибо! Получилось