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.
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
Stored procedure without output parameter:
CREATE PROCEDURE Getstudentname
(#studentid INT -- Input parameter, Studentid of the student
)
AS
BEGIN
SELECT Firstname + ' ' + Lastname
FROM tbl_Students
WHERE studentid = #studentid
END
I executed the above stored procedure:
exec Getstudentname 2
Result is: Pankaj Kumar
where as stored procedure with output parameter:
CREATE PROCEDURE GetstudentnameInOutputVariable
(#studentid INT, -- Input parameter, Studentid of the student
#studentname VARCHAR(200) OUT -- Out parameter declared with the help of OUT keyword
)
AS
BEGIN
SELECT #studentname = Firstname + ' ' + Lastname
FROM tbl_Students
WHERE studentid = #studentid
END
I executed this stored procedure:
DECLARE #return_value int
EXEC #return_value = [dbo].[Getstudentname]
#studentid = 2
SELECT 'Return Value' = #return_value
GO
Result is: Pankaj Kumar
Your procedure may not select anything. Your OUT param could, for example, store the number of rows inserted into a programmatically created table. That output could then be the input for a subsequent EXEC.
Correct way to execute GetstudentnameInOutputVariable
DECLARE #return_value VARCHAR(200)
EXEC [dbo].[Getstudentname] 2,#return_value OUTPUT
SELECT 'Return Value' = #return_value
GO
There can be few number of scenario where you can use OUTPUT paramter,your example do not seem to be one of them.
When your proc return one value which normally indicate Flag .
Like in your example,
SELECT #studentname = Firstname + ' ' + Lastname FROM tbl_Students
I may need to select several column from tbl_Students ,so output column is not ideal.
Say I have a proc which does DML operation and in front end application like c#
I want only flag to know if given proc wa executed succesfully or some validation fail or some error,I need to know only Pass/Fail flag and will do work based on flag.
When you do DML operation only then ExecuteNonQuery is best choice.
Output paramter work best with EXecuteNonQuery.
So you should use Output parameter in this siutation.
Another situation is when you execute one proc and need 1 or 2 result of that proc to work in calling proc.
Then it is very convnient way.
You can return resultset also but to catch resultset you need to create temp
,it is little cumbersome.
Conclusion : It depend upon your real situation.
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" )
I want to use SQL Server 2014 stored procedure in Report Builder.
ALTER PROCEDURE [dbo].[getCharacterDetails]
#id int
,#name VARCHAR(200) = '' OUTPUT
,#level int = 0 OUTPUT
AS
BEGIN
SELECT
#name = name, #level = level
FROM
dbo.Characters
WHERE
id = #id;
RETURN
END
I want to use #id as an input parameter and name and level as output only parameters.
When I call the procedure using Report Builder I get no data.
When I execute a query:
exec [dbo].[getCharacterDetails] #id= 1;
I also get no result values, only (1 row(s) affected) or Commands completed successfully.
The table Characters contains 1 row with id=1, so the select is correct.
Am I doing it right? Should I be using stored procedures or maybe UDF?
I'm new to SQL Server.
No, this stored procedure definitely doesn't return rows - because you're capturing the values into output parameters.
If you want to return rows - do not assign those columns to variables! Just write the select"as is".
So just this this SELECT statement instead:
SELECT
name, level
FROM
dbo.Characters
WHERE
id = #id;
and your stored procedure will now return a result set of data.
Try this:
ALTER PROCEDURE [dbo].[getCharacterDetails]
#id int
,#name VARCHAR(200) = '' OUTPUT
,#level int = 0 OUTPUT
AS
BEGIN
SELECT
name, level
FROM
dbo.Characters
WHERE
id = #id;
RETURN
END
Try running the stored procedure as:
declare #name VARCHAR(200);
declare #level int;
exec dbo.getCharacterDetails 1, #name output, #level output;
select #name, #level;
You have to provide the arguments and then look at them afterwards. That is how output parameters work.
If you want the stored procedure to return rows, then don't assign the values, or you can do both. The body could be:
SELECT #name = name, #level = level
FROM dbo.Characters
WHERE id = #id;
SELECT #name, #level;
And, for such a simple process, you might want just a view or user defined function.
Your data is cached, delete the data file (rdl.data) it's in the same folder your rdl is in
The name and level parameters serve no purpose. Remove them.
ALTER PROCEDURE [dbo].[getCharacterDetails]
#id int
AS
BEGIN
SELECT
name, level
FROM
dbo.Characters
WHERE
id = #id;
RETURN
END
call it like this:
EXEC [dbo].[getCharacterDetails] #ID
and make sure you map a SSRS parameter to the input parameter #ID in the parameters tab.
If you still don't get anything use SQL Profiler to work out what parameter it's passing in.
Why isn't my insert procedure returning the ID of the newly inserted row? AND, when calling this procedure, why do I have to supply a value for #EventId? That column is a PK with IDENTITY.
IF OBJECT_ID ( 'vjsql.EventsINSERT', 'P') IS NOT NULL
DROP PROCEDURE EventsINSERT
GO
CREATE PROCEDURE EventsINSERT
#EventId int OUTPUT,
#EventDate datetime,
#Title varchar(100),
#IsActive bit
AS
BEGIN
INSERT INTO EventCalendar ( EventDate, Title, IsActive)
VALUES ( #EventDate, #Title, #IsActive)
SELECT #EventId = SCOPE_IDENTITY()
END
How are you making a call to the stored procedure?
This SP is returning the value of EventID by means of using OUTPUT parameters.
i.e. In programming terms, this is a procedure (not a function) that accepts an OUTPUT parameter which will be set with the value during the execution of the stored procedure.
For this, you will have to pass the variable for #EventID. The value of which will be set within the procedure and you will be able to read the value of it, once the procedure has finished.
See the example code below.
DECLARE #NewEventID INT
EXEC EventsINSERT
#EventId = #NewEventID OUTPUT,
#EventDate = '08/04/09',
#Title = 'Hello World',
#IsActive = 0
SELECT #NewEventID
Try adding some statement terminators:
BEGIN
INSERT INTO EventCalendar ( EventDate, Title, IsActive)
VALUES ( #EventDate, #Title, #IsActive);
SELECT #EventId = SCOPE_IDENTITY();
END
AND, when calling this procedure, why do I have to supply a value for #EventId? That column is a PK with IDENTITY.
You don't, but you do need to supply a variable of type int (or compatible with int) for the output value to be put into.
You don't need to specify a value for the OUTPUT parameter, you need to specify which local variable the output gets put into:
By default, SQL Management Studio names the parameter and the variable the same, which can be confusing. Here's an example of your SP being called:
DECLARE #InsertedEventId int
EXEC [dbo].[EventsINSERT]
#EventId = #InsertedEventId OUTPUT,
#EventDate = N'2009-08-05',
#Title = N'Some event',
#IsActive = 1
-- Display ID as result set
SELECT #InsertedEventId
Just to clarify: your stored procedure is fine. I used it as-is.
Why isn't my insert procedure
returning the ID of the newly inserted
row?
Your code should work. Try in the console instead of
SELECT #EventId = SCOPE_IDENTITY()
doing
SELECT SCOPE_IDENTITY()
and view what happens. Is possible that you are calling it the wrong way. You should store the value of the OUTPUT variable in a variable in the scope where you call this SP.
when calling this procedure, why do I
have to supply a value for #EventId?
Because you have to supply a value for every parameter you have. It doesn't matter if is a real value, it will be discarded, but you must call the stored procedure with a variable in this parameter to catch the returned value.
I'm pretty rusty with tsql, but don't you need to explicitly select ##identity to get that row id? That's where i'd go digging as I think scope_identity() may not return a value in the context of a user function/procedure.