How use output parameter from procedure?
Assuming that you have a sproc with an output parameter. You can call it and read the output value.
CREATE PROCEDURE AddOne
#val int,
#valPlusOne int out
AS
BEGIN
SET NOCOUNT ON;
SET #valPlusOne = #val + 1
END
GO
--This is used to store the output
declare #PlusOne int
--This calls the stored procedure setting the output parameter
exec AddOne 1, #valPlusOne=#PlusOne OUT
--This displays the value of the output param
select #Plusone
Related
Is it possible to declare and assign the result from an sp in one statement?
If we can do,
DECLARE #lastid INTEGER = (SELECT MAX([Id]) FROM [ADVWKS].[dbo].[Account]);
why cant we do the below one?
DECLARE #lastaccid INTEGER = (EXEC sp_GetGlobalVarVal 'LAST_ACCID');
So far you need one statement to declare the variable and one to execute the procedure and assign the variable as return, output, cursor etc. Can we do it in a single statement??
You cannot do the below because it is often the case that the return type for the stored procedure is a table. It would either execute, and run DML statements which wont return anything, or it would return data set. Now what you can do is to build a scalar value function instead of stored procedure and select from that. https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql?view=sql-server-2017
Another possibility is using: https://learn.microsoft.com/en-us/sql/relational-databases/stored-procedures/return-data-from-a-stored-procedure?view=sql-server-2017#returning-data-using-a-return-code, but conceptually per article, this is only used for Return Codes, not really for the variable assignment or populating variables for different types, and do not forget, there is only values between 0 - 4.
Use an OUTPUT parameter instead. This is very simplified, however:
CREATE PROC TEST_SP #I int, #O int OUTPUT AS
SET #O = #I *5;
GO
DECLARE #Var int;
EXEC TEST_SP 1,#Var OUTPUT;
SELECT #Var;
GO
--CLEAN UP
DROP PROC TEST_SP;
You can use OUTPUT Parameters:
This is an example SP where the second variable (#Param_2) is defined as output Parameter
CREATE PROCEDURE [dbo].[MySP]
(
#Param_1 int,
#Param_2 int output
)
AS
BEGIN
SET NOCOUNT ON;
SET #Param_2 = 10 * #Param_1;
END
Now you can call the SP like this:
DECLARE #Value_1 INT = 42;
DECLARE #RetVal INT = 0;
SELECT #RetVal;
EXEC [dbo].[MySP] #Value_1, #Param_2 = #RetVal OUTPUT;
SELECT #RetVal;
The frist time you select #RetVal the value will still be 0. After the execution of the SP, #RetVal will be 420 (10 * 42 calcualted inside the SP):
-----------
0
(1 row affected)
-----------
420
(1 row affected)
TLDR: No :)
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 the following stored procedure which returns the file size in bytes per input full file path string.
I want to write another wrapping stored procedure or function to update a table that has two columns. Column Full_Path (input for the stored procedure below) and column size (should be updated by the output from the procedure below). Basically I want to update the size column per each file (specified in the full path column) using the procedure below.
I am not sure how to do it. Please advise.
Create proc sp_get_file_size (#fileName varchar(200))
as
begin
declare #ntcmd varchar(200)
declare #detailLine varchar(200)
declare #pos1 int
declare #pos2 int
declare #size int
set nocount on
Create table #res (line varchar(400))
set #ntcmd = 'dir /-C ' + #fileName
insert #res exec xp_CmdShell #ntcmd
select #detailLine = line
from #res where rtrim(ltrim(line)) like '%bytes'
-- if detail Line is null - return -1
if isnull (#detailLine ,'*') = '*' return -1
-- get position of words bytes and File(s)
set #pos1 = charindex ('bytes' ,lower(#detailLine))
set #pos2 = charindex ('(s)' , lower(#detailLine))
-- extract the size value from the details Line
set #size = convert (int, rtrim(ltrim(
substring (#detailLine , #pos2+3,#pos1 - #pos2 - 4))))
return (#size)
set nocount off
end
go
Rewrite your stored procedure as scalar used-defined function that returns the size. Then you don't need a second stored procedure, a single update statement will do the trick:
UPDATE MyTable SET Size = fn_get_file_size(Full_Path)
(note: you will need to use table variable instead of temp table #res since temp tables aren't allowed inside of UDFs)
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.