i have the following stored procedure to test scope of variables
alter proc updatePrereq
#pcntr int,
#pmax int
as
begin
if #pcntr = 1
begin
declare #i int
set #i= #pcntr
end
set #i=#pcntr
select #i;
end
go
In the above script #i is declare in the if block only when the #pcntr value is 1. Assume the above stored procedure is called 5 times from this script
declare #z int
set #z = 1
declare #max int
set #max = 5
while #z <= #max
begin
exec dbo.updatePrereq #z, #max
set #z = #z + 1
end
go
As i said earlier #i variable exists only when #pcntr is 1. Therefore when i call the stored procedure for the second time and so forth the control cannot enter the if block therefore #i variable wouldn't even exist. But the script prints the value in #i in each iteration, How comes this is possible should it throw an error saying #i variable does not exist when #pcntr values is greater than 1?
here is video showing this issues
thanks
The scope of a variable is the range of Transact-SQL statements that can reference the variable. The scope of a variable lasts from the point it is declared until the end of the batch or stored procedure in which it is declared. (Source :MSDN)
Its scope doesn't end at the If satement
Related
Recently, we monitored a very strange issue about SQL Server session block, there was a scalar function for string splitting process which used in a loop code block within the stored procedure, we've checked the code, there was no any operations on database tables, why other sessions were blocked by the session that invoking the function?
here is the function definitions:
CREATE function [dbo].[splits](#SourceSql varchar(max), #StrSeprate varchar(10), #y int)
returns varchar(max) as
begin
declare #i int
declare #idx int
declare #s varchar(max)
if(right(#SourceSql,1)!=#StrSeprate)begin
set #SourceSql=#SourceSql+#StrSeprate
end
set #idx=0
set #i=charindex(#StrSeprate,#SourceSql)
while #i>=1
begin
set #s=left(#SourceSql,#i-1)
set #SourceSql=substring(#SourceSql,#i+1,len(#SourceSql)-#i)
set #i=charindex(#StrSeprate,#SourceSql)
set #idx=#idx+1
if (#idx=#y) begin
break
end
set #s=null
end
return #s
end
This function can not cause the blocking on your instance. You can check other previous operation over same Session\Request.
But, I can help you with other option, instead of this function. Below one is for Splitting Integer values but you can use for VARCHAR as well with changing the datatype.
DECLARE #ids NVARCHAR(MAX) = N'115676,115678,115679,115680,115681'
DECLARE #input_xml XML
SELECT #input_xml = Cast('<root><x>'+ Replace(#ids, ',', '</x><x>')+ '</x></root>' AS XML)
SELECT f.x.value('.', 'BIGINT') AS Id
FROM #input_xml.nodes('/root/x') f(x)
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 :)
So here is the code for the stored procedure and my execution. I keep getting this message when I try to execute my command:
Msg 8146, Level 16, State 2, Procedure sp_LabelFilm, Line 0
Procedure sp_LabelFilm has no parameters and arguments were supplied.
Any idea why? I am trying to update a column in the table tblfilm to say if a movie is short, medium, or long based on its run time.
ALTER PROCEDURE [dbo].[sp_LabelFilm]
AS
BEGIN
DECLARE #Minutes INT, #duration char(10)
DECLARE Filmcursor CURSOR FOR
(SELECT filmruntimeminutes, Duration FROM tblFilm)
OPEN filmcursor
FETCH NEXT FROM filmcursor INTO #duration
WHILE (##FETCH_STATUS = 0)
BEGIN
SELECT #Minutes = FilmRunTimeMinutes FROM tblFilm
IF #Minutes < 120
SET #duration = 'short'
ELSE IF #Minutes < 150
SET #duration = 'medium'
ELSE
SET #duration = 'long'
FETCH NEXT FROM filmcursor INTO #duration
UPDATE tblFilm
SET Duration = #duration
END
CLOSE filmcursor
DEALLOCATE filmcursor
END
DECLARE #Minutes INT, #duration CHAR(10)
EXECUTE [dbo].[sp_LabelFilm] #minutes, #duration
the error means exactly what it says. That you are passing arguments (variables #minutes and #duration) but there are no parameters defined on the stored procedure.
To declare parameters (input variables) you actually declare them before the AS like so:
use Movies
go
alter PROC [dbo].[sp_LabelFilm]
#Minutes INT
,#duration CHAR(10)
AS
BEGIN
DECLARE Filmcursor CURSOR
......
Notice you don't need to use the key word DECLARE and once they are a declared as parameters you don't actually need to declare them again.
Next I am not totally sure what you are attempting to accomplish with the parameters in the stored procedure but it actually looks like you don't want to pass them but rather you want to get them as out put which would be like this:
use Movies
go
alter PROC [dbo].[sp_LabelFilm]
#Minutes INT OUTPUT
,#duration CHAR(10) OUTPUT
AS
BEGIN
DECLARE Filmcursor CURSOR
....
And your execution statement would look like this:
declare #Minutes INT, #duration char(10)
execute [dbo].[sp_LabelFilm] #minutes = #Minutes OUTPUT, #duration = #duration OUTPUT
I had defined the parameters on the stored procedure, before the AS, but still I was facing the same problem until I realized that the procedure had 'create' instead of 'alter'. Changing it to alter procedure worked for me.(Faced this issue while I was trying to debug).
Apart from the first answer which is apt - In my case I did not have any parameters and while EXEC was getting a similar error.
However the difference being - I was putting a "go" below the EXEC statement.
After removing the go it was executed properly.
Is it possible to assign a value to a variable, pass it to a stored proc, and then within the stored proc: 1.) use the value passed in, 2.) change the value assigned to the variable, and 3.) pass the variable back out?
I am attempting to setup a "Time Hack" that I can intersperse throughout procedures to check which statements are running slowly. Below is an example of the procedure and a call to it.
CREATE PROC [dbo].[usp_TIME_HACK_TEST]
#TITLE VARCHAR(255),
#START_TIME DATETIME OUT
AS
BEGIN
PRINT #TITLE + ': ' + RIGHT(CONVERT(VARCHAR(50),GETDATE() - #START_TIME,13),12)
SET #START_TIME = GETDATE()
END
GO
DECLARE #x int = 0
DECLARE #T DATETIME = GETDATE()
Print '#T Value at Beginning: ' + convert(VARCHAR(50),#T,21)
WHILE #x < 1000000
SET #x += 1
EXEC usp_TIME_HACK_TEST
#TITLE = 'Test Run',
#START_TIME = #T
Print '#T Value at End: ' + convert(VARCHAR(50),#T,21)
Here's what the result looks like. It looks like #START_TIME got treated as an input variable, and the procedure did not change the value of #T. Why did this happen if the variable was declared as an output variable?
#T Value at Beginning: 2016-07-27 11:21:19.720
Test Run: 00:00:00:607
#T Value at End: 2016-07-27 11:21:19.720
Thanks in advance for any help.
You need OUT[PUT] in the calling code too.
EXEC dbo.usp_TIME_HACK_TEST
#TITLE = 'Test Run',
#START_TIME = #T OUTPUT
BTW: Regarding your stated goal there are built in DMVs that can be queried to get information about long running statements already, no need to reinvent the wheel...
I am wondering why the table variables inside while loop does not behave like other variables. Table variables created only once and will be used across through out whole looping. but other variables getting initialized every time when loop increases.
Check out the below code for more info
declare #tt int
set #tt =10
while #tt>0
begin
declare #temptable table(id int identity(1,1),sid bigint)
insert into #temptable
select #tt union all
select #tt + 1
select * from #temptable
--delete from #temptable
set #tt=#tt-1
end
is this a bug??
Your premise is wrong. Other variables don't get reinitialised every time the declare statement is encountered either.
set nocount on
declare #tt int
set #tt =10
while #tt>0
begin
declare #i int
set #i = isnull(#i,0) + 1
print #i
set #tt=#tt-1
end
Prints
1
2
...
9
10
As expected
SQL Server variable scope is per batch or the entire function/procedure/trigger, not per black/nested construct
http://msdn.microsoft.com/en-us/library/ms187953.aspx:
The scope of a variable is the range
of Transact-SQL statements that can
reference the variable. The scope of a
variable lasts from the point it is
declared until the end of the batch or
stored procedure in which it is
declared.
Though it is old post just wann add my comments
set nocount on
declare #tt int
set #tt =10
while #tt>0
begin
declare #i int=0
set #i = #i + 1
print #i
set #tt=#tt-1
end
Results:
1
1
1
1
1
1
1
1
1
1
If you want to load the table variable each time the loop executes. DROP FROM #Tablevariable once work done within the loop.