T-SQL: can't seem to set variables immediately after declaring them - sql

I have looked through countless posts on declaring/setting variables in a sql script. But most seem to involve syntax errors or using exec commands. The script I am trying to execute is quite simple - so I am having a hard time understanding why I cant set values.
Here is the SQL:
declare #counter int = 1,
#batchSize int = 100000,
#tableRows int,
#totalBatches int;
set #tableRows= (select count(distinct chFileVersionID) from IRISDocuments)
set #totalBatches = (ceiling((#tableRows / #batchSize)));
--print some stuff
while #counter <= #totalBatches
begin
. . . loop logic that only uses #counter variable for incrementing
end
The error I get is
Must declare the scalar variable "#tableRows"
which is clearly declared directly above. I have tried setting values with a select statement, as well as declaring each variable individually, and declaring and setting value in the same statement with no avail.

The above actually works (I mean testing without the WHILE loop of course).
2 possible source of the error if the variable is not exactly used as above:
Using a variable to define another variable in the same declaration
E.g.:
declare #counter int = 1,
#batchSize int = 100000,
#tableRows int = 10000000000000,
#totalBatches int = (ceiling((#tableRows / #batchSize)))
This won't work and produces your error - within a statement the order of execution is not row-by-row, so you can not give a value to #totalBatches as at that point #tableRows is not implemented yet.
Trying to use a variable that is out of scope
If this is a query that contains the GO batch separator then after that all variables before GO are out of scope.
E.g.:
declare #counter int = 1,
#batchSize int = 100000,
#tableRows int,
#totalBatches int;
SELECT #counter, #batchSize, #tableRows, #totalBatches int
GO
SELECT #counter, #batchSize, #tableRows, #totalBatches int
The second SELECT will throw your error.

Related

TSQL SET command not working inside EXEC statement

I am trying to run the below script in SQL Server Management Studio:
DECLARE #counter INT = 1
EXEC('SET '+#counter+' = '+#counter+' + 1')
However, I get this error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '1'-
I have been troubleshooting for days but gotten nowhere - I guess most likely TSQL simply does not accept this inside an EXEC command, but what could I potentially replace it with?
This is a part of a larger script where the above MUST be inside an EXEC statement in order for the rest to work..
This isn't going to working for a number of reasons.
Let's start with what you have:
DECLARE #counter INT = 1
EXEC('SET '+#counter+' = '+#counter+' + 1')
EXEC is expecting a string inside the parenthesis, yet you have an int (the variable #counter), so that is your first problem. Oddly, however, you aren't getting a conversion error here; it seems that #counter is being interpreted as its value (1), rather than Data Type Precedence being applied first. (If you try SELECT 'SET '+#counter+' = '+#counter+' + 1' you'll get the error "Conversion failed when converting the varchar value 'SET ' to data type int.").
Even if the query successfully got past that hurdle, that would cause another error because the query becomes:
SET 1 = 1 + 1
Obviously that isn't going to work, 1 is 1, no other number.
Next, a variable declared outside of the scope of a "dynamic" statement can't be referenced inside it. Let's take the following:
DECLARE #Varible int = 1;
EXEC (N'SELECT #Variable;');
This returns the error:
Must declare the scalar variable "#Variable".
If you are using a "dynamic" statement (your statement isn't dynamic at all), then you need to parametrise the variables. This is why you shouldn't be using EXEC ({SQL Statement}) and should be using sys.sp_executesql.
So, for the above, you get:
DECLARE #counter INT = 1;
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SET #Variable = #Variable + 1;';
EXEC sys.sp_executesql #SQL, N'#Variable int OUTPUT',#Variable = #counter OUTPUT;
SELECT #Counter;
As I mentioned, that is all pointless, as there's nothing dynamic in the above, and your query should just be:
DECLARE #counter INT = 1;
SET #counter = #counter + 1;
Of course, none of this even goes on to address things like how to safely inject objects into a dynamic statement, but that isn't what this question is about. As I mention, the dynamic statement here is redundant, as it isn't dynamic, and it's not that required deferred parsing (such as when you ALTER a table to add a column, and then try to reference that column in the same batch).
If you run the script below, it'll throw an error like
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#counter".
BEGIN
DECLARE #counter INT = 1
EXEC('SET #counter = '+ #counter+' + 1')
END
WHY?
Because dynamic SQL is a different scope to the outer.
So can try this way (using output)
DECLARE #counter INT = 1
DECLARE #SQL nvarchar(50) = 'SELECT #counter2 =' + CAST(#counter + 1 AS VARCHAR(100))
exec sp_executesql #SQL, N'#counter2 int out', #counter out
SELECT #counter

Simplest way to insert variables into SQL Stored Procedure using Python?

I have a stored SQL procedure I would like to generalize. This involves passing the Table name, a start time, and an end time through as variables, so one doesn't have to edit the procedure every time. I'm trying to use basic python variables to prompt the user. (Start = int(input("Enter Start Time: "))
I just haven't found a simple way to do this. I've used 'execute sp_execute_external_scripts' and have been seeing if Pyodbc is the right tool for this, but nothing so far has worked, and I didn't fully understand the MS documentation/tutorial for creating a wrapper. So what is the easiest way to prompt the user for input that can be injected into a procedure as a variable? I feel like I'm missing something very simple here.
Upon reading comments here, my original method is overbroad and would require dynamicsql, which I'm not automatically opposed to, but it seems safer to create procedures for individual tables. So I should only need to push Start, End and ChunkSize as integer variables into the procedure.
SOLVED: I got the desired results by writing a small python program using pyodbc. Thank you for the help and gentle nudging away from my original, naive idea.
import pyodbc
connection = pyodbc.connect('Driver={SQL Server};'
'Server=CROWN;'
'Database=ControlInformation;'
'Trusted_Connection=yes;')
cursor = connection.cursor()
Start = int(input("Enter Start Time: "))
End = int(input("Enter End Time: "))
ChunkSize = int(input("Enter # of files to delete at once: "))
cursor.execute('exec PurgeCurrencyExchangeRates #start = %d, #end = %d,
#ChunkSize = %d' %(Start, End, ChunkSize))
cursor.commit(),
connection.close()
SQL stored procedure
CREATE PROCEDURE UserInput
#r int = 1,
#Start bigint = 0,
#End bigint = 0,
#TableName varchar(40) = 'CurrencyExchangeRates',
#ChunkSize int = 10000,
#ColumnName varchar(40) = 'Timestamp'
AS
BEGIN
SET NOCOUNT ON;
while #r > 0
delete top(#ChunkSize)
ControlInformation.dbo.#TableName
where #ColumnName > #Start and #ColumnName < #End
set #r = ##ROWCOUNT
The stored procedure should be something like:
use CurrencyExchangeRates
go
CREATE PROCEDURE PurgeCurrencyExchangeRates
#Start bigint = 0,
#End bigint = 0,
#ChunkSize int = 10000
AS
BEGIN
SET NOCOUNT ON;
declare #r int = 1;
while #r > 0
begin
delete top(#ChunkSize)
from dbo.CurrencyExchangeRates
where [Timestamp] >= #Start and [Timestamp] < #End
set #r = ##ROWCOUNT
end
end
Maybe you need in python:
Start = int(input("Enter Start Time: "))
sql = 'exec UserInput #start =?'
cursor.execute(sql, Start)
cursor.commit()
If you really want to make the table name dynamic then you'll need to use dynamic SQL, like so:
ALTER PROCEDURE DeleteFrom
#tableName sysname ='Table0'
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql nvarchar(4000);
SET #sql = N'DELETE TOP (1) FROM ' + QUOTENAME(#tableName);
EXEC(#sql);
END

Must declare the scalar variable error when calling stored proc

I'm having trouble getting this SP call to work. It worked previously and no changes were made, but I cannot get it to work for the life of me now.
The error code I'm getting is:
Error code 137, SQL state S0002: Must declare the scalar variable "#AppName".
This is using netbeans sql editor. if I hard code the AppName variable, it then throws the same error for AppVer. A coworker can use the exact code and it works fine in his non-netbeans ide (I think vis studio?). This leads me to believe that it's a netbeans issue rather than the code itself.
Any ideas?
DECLARE #AppName nvarchar(100) = 'Excel ASAP Utilities';
DECLARE #AppVer nvarchar(50) = '3.07b';
DECLARE #AppManufacture nvarchar(100) = null;
DECLARE #RequestedByID dbo.UserID = 'userid';
DECLARE #RequestDescription nvarchar(3500) = 'This is a test';
DECLARE #NumberNeededFor int = null;
DECLARE #AssignToID dbo.UserID = '?';
DECLARE #RequestType_ID int = 6;
DECLARE #LCM_ID int = 50;
DECLARE #ProcessID int = 3;
DECLARE #RM_ID int;
EXECUTE dbo.Request_InsertNewShortForm
#AppName,
#AppVer,
#AppManufacture,
#RequestedByID,
#RequestDescription,
#NumberNeededFor,
#AssignToID,
#RequestType_ID,
#LCM_ID,
#ProcessID,
#RM_ID OUTPUT;
SELECT 'Return Value' = #RM_ID;

resize SQL Varchar

Is there a way to resize a local varchar in sql?
like:
DECLARE #MYVARIABLE AS VARCHAR(3)
REDIM #MYVARIABLE(50)
That will change the size of #MYVARIABLE to 50 char.
Note: I need to keep the variable because I use it after.
So I cant use another one.
Sample 2:
DECLARE #MYVARIABLE AS VARCHAR(4)
SET #MYVARIABLE = 'TEST'
SET #MYVARIABLE = '12345'
SELECT #MYVARIABLE
here, MYVARIABLE = '1234' at the end. should be '12345'
how to do it (and keep myvariable)
can I delete the current and create a new one?
Set you variable to the largest size you know you will need in the first place e.g.:
DECLARE #MYVARIABLE AS NVARCHAR(50)
Or if you don't know how big you need then you could always use MAX:
DECLARE #MYVARIABLE AS NVARCHAR(MAX)
If the length of the variable is important to you then you can get that after it is assigned a value:
DECLARE #MYVARIABLE AS NVARCHAR(MAX)
DECLARE #LENGTHOFMYVARIABLE AS INT
SET #MYVARIABLE = 'somerandomtext'
SET #LENGTHOFMYVARIABLE = LEN(#MYVARIABLE)
In this case #LENGTHOFMYVARIABLE would be 14.
UPDATE
The only other way I know of how to do this is to use a secondary temp variable:
DECLARE #MYVARIABLE AS NVARCHAR(3)
DECLARE #MYBIGGERTEMPVARIABLE AS NVARCHAR(50)
SET #MYBIGGERTEMPVARIABLE = #MYVARIABLE
Obviously there are some limitations if the variable gets passed along again, but apart from that the only other way would be to change the source variable length to be a more reasonable value. Really there isn't much of a reason to have variables of such a small size.
you can use temp variable :
DECLARE #MYVARIABLE AS VARCHAR(3)
DECLARE #myVal as VARCHAR(50)=#MYVARIABLE
No, you cannot alter the definition of a variable, nor undeclare a variable. It's definition is fixed, from the point it is declared, until the end of the batch.
It's definition even takes effect if the declaration doesn't appear to run:
if 1 = 0
begin
declare #a varchar(3)
print 'declared!'
end
else
begin
print 'not declared?'
end
set #a = 'abc'
print #a
prints:
not declared?
abc
Since you can't change the way that the variable is being declared, you'll have to give up on this attempted approach to your overall problem (but I can't offer suggestions, since you've not described your overall problem)

Why is variable declared inside IF statement created even when condition evaluates to false?

Since #A is never declared, sql server should throw an error, but it doesn’t. Why is that?
DECLARE #i int = 1;
IF #i > 10
BEGIN
DECLARE #A int = 100;
END
PRINT #A; // doesn't return any result
thanx
SQL Server does not have block level variable scoping.
It's per batch/stored proc etc
From MSDN (my bold)
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.
gbn's answered the question, but on a related note, watch out for code like this :
DECLARE #i INT = 0
WHILE #i < 2
BEGIN
DECLARE #a VARCHAR(100)
IF #i = 0
SET #a = 'changed'
PRINT COALESCE(#a, 'is null')
SET #i = #i + 1
END
When run, it prints "changed" twice, whereas many people would probably expect it to print "changed", then "is null". Be careful when declaring variables inside loops. You might want to explicitly set it to NULL in the declaration. e.g.
DECLARE #a VARCHAR(100) = NULL
When this modified version is run, it displays "changed" then "is null".