I'm facing this error " must declare the scalar variable #return " in PowerBuilder 9 running on SQL server 14. When I'm executing the stored procedure using the SQL management studio it is returning 10000 as expected. But while calling this SP from PowerBuilder I'm facing the error. Any suggestions are appreciated. Thanks
Function in PowerBuilder code:
Declare sp_v procedure for
#return = proc_v_sp
#eid = :p_eid,
#year = :p_year,
#bid = :p_bid,
#hid = :p_hid
using sqlca;
Execute sp_v;
IF SQLCA.SQLCode <> 0 THEN
lReturn = SQLCA.SQLCode
ELSE
FETCH sp_v INTO :lReturn;
END IF
CLOSE sp_v;
In SQL SERVER SP:
Alter procedure proc_v_sp
#eid int,
#year int,
#bid varchar(8),
#hid char(3)
As
Begin
Declare #count int,
Declare..............
..........ignoring as it is long SP...........
Select #count = count(*)
from sy_e
where sy_e_eid = #eid and sy_e_year= #year
IF #count >0
RETURN 20000
ELSE
RETURN 10000
END
Looking at the documentation, I don't see anything about how to get a RETURN value back from an executed stored procedure. The documentation lists the syntax for the SP declaration in PB as...
DECLARE logical_procedure_name PROCEDURE FOR
SQL_Server_procedure_name
#Param1 = value1, #Param2 = value2,
#Param3 = value3 OUTPUT,
{USING transaction_object} ;
So that's why you're getting the syntax error. It's just not expecting #return there. If you can change the stored procedure, then you should be able to use an OUTPUT parameter. After a bit of googling, it looks like you would still have to use FETCH after the EXECUTE to get the variable specified for the output parameter populated.
I came back to this once I had PB in front of me because I was curious if it was possibles. After a bit of experimentation and looking at the MSDN docs for RETURN, I was able to get the return value populated in the returnValue variable using the code below.
long returnValue
DECLARE sp_test PROCEDURE FOR
#return_status = sp_test_return
USING SQLCA;
EXECUTE sp_test;
FETCH sp_test INTO :returnValue;
Here's the stored procedure.
CREATE PROCEDURE [dbo].[sp_test_return]
AS
RETURN 159
GO
You're making this all way too hard...
Change your RETURN to SELECT, and use a stored procedure datawindow. You can then rip out all that code, and replace it with one line.
dw.retrieve( args )
And your return value will be dw.getItemNumber( 1, "return_status")
-Paul-
I don't have sample code.
But it's super easy... Your SP needs to return a result set instead of a return value - even if that result set is a single value on a single row.
Change the RETURN to SELECT. That returns a result set.
Now, create a datawindow and select Stored Procedure as the datasource. Then select your sp as the source. Test it by providing the values for arguments and seeing if it returns the result you're looking for.
From here, it's just PB code.
datastore myDW
myDW = create datastore
myDW.setTransObject( SQLCA )
myDW.retrieve( args... )
theResult = myDW.getItemNumber( 1, "return_status" )
Related
I have a stored procedure A on server 1 that takes 2 parameters from the user, and then using a linked server (ew), pulls in the results (a table) from server 2.
ALTER PROCEDURE [DW].[StoredProcA]
#InvFromDate date OUTPUT,
#InvToDate date OUTPUT
AS
WITH CTE_Labor AS
(
SELECT blabla
FROM LinkedServer.Database.schema.table
<lots more ctes, etc.>
For performance, I'd like to instead have a stored procedure A still accept the 2 parameters, but then pass them on to stored procedure B that sits on Server 2, and return those results back to the user.
Say - I can put the stored procedure on server 2, and call it from Server 1
DECLARE #return_value int
EXEC #return_value = [LinkedServer].[DB].[Schema].[StoredProcB]
#InvFromDate = '2022-10-01',
#InvToDate = '2022-10-31'
That works.
But I'm not clear on the syntax to do the above, but have those 2 parameters be entered by the user in stored procedure 1.
Clearly this attempt is wrong:
ALTER PROCEDURE dbo.StoredProc1
#InvFromDate DATE,
#InvToDate DATE
AS
BEGIN
DECLARE #return_value int;
EXEC #return_value = [LinkedServer].[DB].[Schema].[StoredProcB]
#InvFromDate = #InvFromDate,
#InvToDate = #InvToDate;
RETURN #return_value;
END
Edit: Maybe this attempt isn't wrong.
It works when I right click and run the stored procedure, returning both the desired table and Return Value = 0. It just doesn't work when I point our front-end GUI at it. But that might not be a question for here.
Since you are already using a linked server you could utilise this openquery approach Insert results of a stored procedure into a temporary table
Noting the following:
OPENQUERY/ linked servers are generally bad but I'm sure you're all over this
parameter string concatenation is bad
Your wrapper proc has output parameters but I don't see any reason for it... so I've removed them. See if it makes a difference.
--
ALTER PROCEDURE [DW].[StoredProcA]
#InvFromDate date,
#InvToDate date
AS
DECLARE #sql VARCHAR(4000)
SET #sql = 'EXEC [DB].[Schema].[StoredProcB] #InvFromDate = ''' + FORMAT(#InvFromDate + 'yyyy-MM-dd') + ''',#InvToDate = ''' + FORMAT(#InvToDate,'yyy-MM-dd') + ''''
PRINT(#sql) -- for degbugging cause this never works first time
SELECT *
INTO #tmpTable
FROM OPENQUERY([LinkedServer], #SQL)
SELECT * FROM #tmpTable
Got it.
1.) For this method, have to go into the Linked Server, and set [Enable Promotion of Distribution Transaction] = FALSE.
2.) Syntax
Alter proc [dbo].[999_Test]
#InvFromDate date
,#InvToDate date
as
IF OBJECT_ID('tempdb..#tmpbus') IS NOT NULL drop table #tmpbus;
CREATE TABLE #tmpBus
(
Column 1 (datatype),
Column 2 (datatype),
etc. )
INSERT INTO #tmpBus
EXEC [LinkedServer].[DB].Schema.[StoredProcInLinkedServerO]
#InvFromDate,
#InvToDate;
select *
from #tmpBus
GO
When exactly do we use stored procedures with output parameters and when do we use stored procedures without parameters?
I base my question on an example:
Stored procedure with output parameter
CREATE PROCEDURE uspGetContactsCountByCity
#City nvarchar(60),
#ContactsCount int OUT
AS
BEGIN
SELECT #ContactsCount = COUNT(ContactID)
FROM Contacts
WHERE City = #City
END
Stored procedure executing
DECLARE #ContactsTotal INT
EXEC uspGetContactsCountByCity #ContactsCount = #ContactsTotal OUT, #city = 'Berlin'
SELECT #ContactsTotal
Results: 2
Stored procedure without output parameter
CREATE PROCEDURE uspGetContactsCountByCity2
#City nvarchar(60)
AS
BEGIN
SELECT COUNT(ContactID)
FROM Contacts
WHERE City = #City
END
Stored procedure executing:
EXEC uspGetContactsCountByCity2 #city = 'Berlin'
Results: 2
Both procedures return the same result, in same form, so what's the difference?
Basically, the result you're seeing is actually the result of your SELECT at the end of the procedure, which is doing the same thing.
Please take a look at this documentation:
If you specify the OUTPUT keyword for a parameter in the procedure definition, the stored procedure can return the current value of the parameter to the calling program when the stored procedure exits. To save the value of the parameter in a variable that can be used in the calling program, the calling program must use the OUTPUT keyword when executing the stored procedure.
So basically if you would like your stored procedure to just return just a value instead of a data set, you could use the output parameter. For example, let's take the procedures you have given as an example. They both do the same thing, this is why you got the same result. But what about changing a little bit in the first procedure that has the output parameter.
Here's an example:
create table OutputParameter (
ParaName varchar(100)
)
insert into OutputParameter values ('one'), ('two'),('three'),('one')
CREATE PROCEDURE AllDataAndCountWhereOne
#name nvarchar(60),
#count int OUT
as
Begin
SELECT #count = COUNT(*) from OutputParameter
Where ParaName = #name
select Distinct(ParaName) from OutputParameter
End
Declare #TotalCount int
Exec AllDataAndCountWhereOne #count = #TotalCount OUT, #name = 'One'
Select #TotalCount
With this example, you are getting all the distinct stored data in the table, plus getting the count of a given name.
ParaName
--------------------
one
three
two
(3 row(s) affected)
-----------
2
(1 row(s) affected)
This is one way of using the output parameter. You got both the distinct data and the count you wanted without doing extra query after getting the initial data set.
At the end, to answer your question:
Both procedures gives us the same result, in same form, so what's the difference?
You didn't make a difference in your own results, this is why you didn't really notice the difference.
Other Examples:
You could use the OUT parameter in other kinds of procedures. Let's assume that your stored procedure doesn't return anything, it's more like a command to the DB, but you still want a kind of message back, or more specifically a value. Take these two examples:
CREATE PROCEDURE InsertDbAndGetLastInsertedId
--This procedure will insert your name in the database, and return as output parameter the last inserted ID.
#name nvarchar(60),
#LastId int OUT
as
Begin
insert into OutputParameterWithId values (#name);
SELECT #LastId = SCOPE_IDENTITY()
End
or:
CREATE PROCEDURE InsertIntoDbUnlessSomeLogicFails
--This procedure will only insert into the db if name does exist, but there's no more than 5 of it
#name nvarchar(60),
#ErrorMessage varchar(100) OUT
as
Begin
set #ErrorMessage = ''
if ((select count(*) from OutputParameterWithId) = 0)
begin
set #ErrorMessage = 'Name Does Not Exist'
return
end
if ((select count(*) from OutputParameterWithId) = 5)
begin
set #ErrorMessage = 'Already have five'
return
end
insert into OutputParameterWithId values (#name);
End
These are just dummy examples, but just to make the idea more clear.
An example, based on yours would be if you introduced paging to the query.
So the result set is constrained to 10 items, and you use a total count out parameter to drive paging on a grid on screen.
Answer from ozz regarding paging does not make sense because there is no input param that implements a contraint on the number of records returned.
However, to answer the question... the results returned by these stored procedures are not the same. The first returns the record count of contacts in given city in the out param ContactsCount. While the count may also be recieved in the second implement through examining the reader.Rows.Count, the actual records are also made a available. In the first, no records are returned - only the count.
I have a simple stored procedure that fetches an int from a table and updates the table.
This is the complete stored procedure
create procedure spGetNewVMacsBatchNumber as
begin
set nocount on
declare #LastUsedVMacsBatchNumber int
update tblSettings with (TABLOCK)
set LastUsedVMacsBatchNumber = isnull(LastUsedVMacsBatchNumber, 0) + 1,
#LastUsedVMacsBatchNumber = isnull(LastUsedVMacsBatchNumber, 0) + 1
select #LastUsedVMacsBatchNumber
end;
When I run this from SSMS it does what I expect from it, return the number + 1 and update the table.
I call it in SSMS like this :
declare #BatchNumber int
exec #BatchNumber = spGetNewVMacsBatchNumber
But when I call it from within another stored procedure it does not returns the value I expect, but always returns zero. The call in the other stored procedure is exact the same as above.
How can I call this stored procedure from another stored procedure and still get the value I expect ?
The idea for spGetNewVMacsBatchNumber is to retrieve the current value + 1 and to make sure it does not returns a wrong value when 2 processes run it at the same time.
Maybe there is a better way of doing that then my approach ?
You should actually use output parameters for your procedure.
alter procedure spGetNewVMacsBatchNumber #ReturnVal int output as
begin
set nocount on
declare #LastUsedVMacsBatchNumber int
update tblSettings with (TABLOCK)
set LastUsedVMacsBatchNumber = isnull(LastUsedVMacsBatchNumber, 0) + 1,
#LastUsedVMacsBatchNumber = isnull(LastUsedVMacsBatchNumber, 0) + 1
select #ReturnVal = #LastUsedVMacsBatchNumber
end;
And then call it like this:
declare #BatchNumber int
exec spGetNewVMacsBatchNumber #ReturnVal= #BatchNumber output;
select #BatchNumber
You are getting the return status of the spGetNewVMacsBatchNumber stored procedure like that, not the actual "return value" from the stored proc.
I have a rather simple stored procedure that needs to return a results set, something like (the code is a highly simplified version of the real one, but is enough to describe my problem):
CREATE PROCEDURE MyProc(#Par1 VARCHAR(100))
AS
BEGIN
SELECT A,B,C FROM MyTable ;
END ;
This, so far, works perfectly as the invoking procedure does get the values A,B and C of all the records in that table.
Now, I need to allow the addition of "conditions" (i.e. WHERE clause) as provided through the received parameter Par1. To do that, I declare a new local variable aimed to hold a full SQL select statement that would include the conditions, so the same procedure would now look like:
CREATE PROCEDURE MyProc(#Par1 VARCHAR(100))
AS
BEGIN
DECLARE #SQLSTT VARCHAR(1000) ;
SET #SQLSTT = 'SELECT A,B,C FROM MyTable WHERE ' + #Par1 ;
EXECUTE ( #SQLSTT );
END ;
This ALMOST work: The correct number of records are affected, but they are not passed to the invoking procedure.
I thought of using a temporary table (or table variable), but it would be an unneeded overhead since, once the records are selected, there is no further manipulation to take place within this procedure and only need to be passed on to the invoking SP.
So, my question is: What should be the correct syntax to achieve what I want?
Thanks in advance.
EDIT
I found the issue. It had nothing to do with the procedure (i.e. MyProc) but rather with the way I was attempting to see the returned results. Quick fix and it all works are needed.
Thanks for all that took the time to walk though my question and send comments/suggestions/answers.
Declare a table to hold the return.
declare #ret table (a int, b int, c int)
insert into #ret
EXECUTE ( #SQLSTT );
You must use # in front of your parameter
The updated procedure is as follows
CREATE PROCEDURE MyProc(#Par1 VARCHAR(100))
AS
BEGIN
DECLARE #SQLSTT VARCHAR(1000) ;
SET #SQLSTT = 'SELECT A,B,C FROM MyTable WHERE ' + #Par1 ;
EXECUTE ( #SQLSTT );
END ;
This works fine. I changed it to use NVARCHAR. Please check how you are calling the procedure with appropriate single quotations:
CREATE PROCEDURE MyProc(#Par1 NVARCHAR(100))
AS
DECLARE #SQLSTT NVARCHAR(1000) ;
SET #SQLSTT = N'SELECT A,B,C FROM MyTable WHERE ' + #Par1 ;
EXEC( #SQLSTT )
Example call:
exec MyProc 'A <>''myvalue'''
I'm new to SQL Server. Currently I'm working on an existing source code and I have some unclear point the stored procedure
For examples:
My_Stored_Procedure_1
CREATE PROC [dbo].[My_Stored_Procedure_1]
#ID INT,
#DATE DATETIME
AS
BEGIN
UPDATE query...
PRINT 'ID is ' + CAST(#ID AS VARCHAR(10))
END
My_Stored_Procedure_2
CREATE PROC [dbo].[My_Stored_Procedure_2]
#RESULT INT
AS
BEGIN
EXEC #RESULT = My_Stored_Procedure_1 // Unclear point
END
My question is that, I don't see any return value from the My_Stored_Procedure_1, so what will be returned to #RESULT variable? Maybe a default returned value of the executed stored procedure?
Thank you very much.
#Result will have default value that was passed while executing the Stored Procedure My_Stored_Procedure_2.
The statement EXEC #RESULT = My_Stored_Procedure_1 will execute with error and terminate the execution of My_Stored_Procedure_2 because you have not passing two input parameter to My_Stored_Procedure_1 sp while calling this.
The default value of a stored procedure is dependent on whether or not the statements in that procedure executed successfully. For example, if your UPDATE statement failed due to a constraint error, #RESULT would be set to -6. If it runs successfully with no error, it would normally be 0. See the following:
CREATE TABLE #testTable (Value INT, CHECK (Value > 5));
INSERT #testTable VALUES (6);
GO
CREATE PROCEDURE #testProc AS
UPDATE #testTable SET Value = 2;
GO
DECLARE #ret INT;
EXEC #ret = #testProc;
PRINT #ret; -- “-6”
DROP PROCEDURE #testProc;
DROP TABLE #testTable;
GO
Note that you never make an attempt to observe the value of #RESULT. Since #RESULT is not an OUTPUT parameter, the caller of "dbo"."My_Stored_Procedure_2" will not see the return value of "dbo"."My_Stored_Procedure_1". It looks like you are ignoring the return value. If you could alter your example to show how you are consuming the return value, that can help us understand your question and provide better advice.