Must declare the scalar variable "#numCount" - sql

I'm getting this error when I'm putting outside the loop in side casting working .
Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "#numCount"
Can you please tell me what I'm doing wrong ??
Thanks
create table #myTemp
(
rowid int identity (1,1),
Name varchar(200),
email varchar(200),
flag bit
)
select * from #myTemp
declare #name varchar(200), #email varchar(200)
declare #numCount int = 20
WHILE (1 <= #numCount)
BEGIN
SET #name = 'My Name '+ CAST(#numCount AS VARCHAR)
SET #email = 'Email'+ CAST(#numCount AS VARCHAR)
INSERT INTO #myTemp(Name, Email, flag)
VALUES (#name, #email, 1)
SET #numCount = #numCount - 1
END
GO
PRINT 'My String' + CAST(#numCount AS VARCHAR) + '***'

Declaring and setting in the same statement is perfectly valid in SQL Server 2008 R2. The issue is your GO
GO -- < -- starts a new batch and thus the declaration on #NumCount is totally unrealted to this Print.
PRINT 'My String' + CAST(#numCount AS VARCHAR) + '***'

It has to do your variable being only accessible in your batch. Get rid of "GO". And it should work.
create table #myTemp (rowid int identity (1,1), Name varchar(200),email varchar(200),flag bit)
select * from #myTemp
declare #name varchar(200),#email varchar(200)
declare #numCount int = 20
while (1 <= #numCount)
begin
SET #name = 'My Name '+ CAST(#numCount AS VARCHAR)
SET #email = 'Email'+ CAST(#numCount AS VARCHAR)
INSERT INTO #myTemp
(Name,Email,flag)
values
(#name,#email,1)
SET #numCount = #numCount - 1
END
PRINT 'My String' + CAST(#numCount AS VARCHAR) + '***'

You can't declare and initialize a variable at the same time like:
declare #numCount int = 20
Instead do something like:
declare #numCount int;
SET #numCount = 20;

Related

SQL Server stored procedure with while condition containing table variable

I have a table where the name of the country changes regularly, like my_table_US_NA, my_table_CAN_NA, my_table_MEX_NA and so on:
create table my_table_US_NA(id int)
insert into my_table_US_NA(id) values (1)
insert into my_table_US_NA(id) values (2)
insert into my_table_US_NA(id) values (3)
insert into my_table_US_NA(id) values (4)
select * from my_table_US_NA
id
----
1
2
3
4
I have a stored procedure like this:
create procedure my_looping_procedure (#Country varchar(10))
as
begin
declare #MyTable varchar(50), #COUNTER int
set #COUNTER = 1
set #MyTable = concat('my_table_', #Country, '_NA')
while (#COUNTER <= (select max(id) from #MyTable))
begin
set #COUNTER = #COUNTER + 1
print #COUNTER
end
end
When I try to compile the procedure, I get this error:
Msg 1087, Level 16, State 1, Procedure my_looping_procedure, Line 15 [Batch Start Line 0]
Must declare the table variable "#MyTable"
I tried moving the while loop into its own little variable:
create procedure my_looping_procedure (#Country varchar(10))
as
begin
declare #MyTable varchar(50),
#sql_loop varchar(max),
#COUNTER int
set #COUNTER = 1
set #MyTable = concat('my_table_', #Country, '_NA')
-- inner variable here
select #sql_loop = '
while (' + #COUNTER + '<= (select max(id) from ' + #MyTable + '))
begin
set ' + #COUNTER + ' = ' + #COUNTER + ' + 1
print ' + #COUNTER + '
end'
exec(#sql_loop)
end
That compiles but returns an error when I try to execute it exec my_looping_procedure:
Msg 245, Level 16, State 1, Procedure my_looping_procedure, Line 16 [Batch Start Line 26]
Conversion failed when converting the varchar value 'WHILE (' to data type int.
I tried declaring and setting all the variables inside #sql_loop:
alter procedure my_looping_procedure (#Country varchar(10))
as
begin
declare #sql_loop varchar(max)
select #sql_loop = '
declare
#MyTable varchar(50),
#COUNTER INT
SET #COUNTER = 1
set #MyTable = concat(''my_table_'', ' + #Country + ', ''_NA'')
WHILE (#COUNTER <= (select max(id) from ' + #MyTable + '))
BEGIN
SET #COUNTER = #COUNTER + 1
print #COUNTER
end'
exec(#sql_loop)
end
This compiles but still errors on execution:
Msg 1087, Level 16, State 1, Line 38
Must declare the table variable "#MyTable".
I then declared the #MyTable variable in the beginning again:
alter procedure my_looping_procedure (#Country varchar(10))
as
begin
declare
#MyTable varchar(50),
#sql_loop varchar(max)
set #MyTable = concat('my_table_', #Country, '_NA')
select #sql_loop = '
declare
#MyTable varchar(50),
#COUNTER INT,
#Country varchar(10),
SET #COUNTER = 1
set #MyTable = concat(''my_table_'', ' + #Country + ', ''_NA'')
WHILE (#COUNTER <= (select max(id) from ' + #MyTable + ' ))
BEGIN
SET #COUNTER = #COUNTER + 1
print #COUNTER
end'
exec(#sql_loop)
end
This actually compiles but complains about the country:
Msg 207, Level 16, State 1, Line 37
Invalid column name 'US'.
Finally, I commented out the initial table set statement:
alter procedure my_looping_procedure (#Country varchar(10))
as
begin
declare
#MyTable varchar(50),
#sql_loop varchar(max)
-- set #MyTable = concat('my_table_', #Country, '_NA')
select #sql_loop = '
declare
#MyTable varchar(50),
#COUNTER INT,
#Country varchar(10),
#MaxCount int
SET #COUNTER = 1
set #MyTable = concat(''my_table_'', ' + #Country + ', ''_NA'')
WHILE (#COUNTER <= (select max(id) from ' + #MyTable + ' ))
BEGIN
SET #COUNTER = #COUNTER + 1
print #COUNTER
end'
exec(#sql_loop)
end
This compiles AND runs, but does nothing.
Can anybody figure out what I'm doing wrong?
Some background:
This is an example of the problem with the parameter and the while loop, not the actual code. As for why it's done this way, the initial design was just for one hard-coded country. When more countries were added, the scripts were copied with new countries hard-coded.
The initial designer is no longer with the company. My current task is just to make a generic piece of code that can be used no matter how many more countries we add. There are hundreds of scripts like this and very little time and few resources on the project.
I genuinely appreciate the suggestions of using a temp table, but the tables are used in other processes. Until we iron out the underlying issues with the process, we are stuck with this design.
Without questioning why you are doing it this way (but those comments are very useful and should be carefully considered). Here is your working code:
create table #my_table_US_NA(id int);
insert into #my_table_US_NA(id) values (1),(2),(3),(4);
declare #MyTable varchar(50), #Country varchar(10);
set #Country = 'US';
set #MyTable = quotename(concat('#my_table_', #Country, '_NA'));
declare #Sql nvarchar(max) = 'declare #COUNTER INT = 1; WHILE (#COUNTER <= (select max(id) from [' + #MyTable + ']))
BEGIN
SET #COUNTER = #COUNTER + 1
print #COUNTER
end';
exec(#Sql);
drop table #my_table_US_NA;
Note 1: I've added quotename as per Larnu's suggestion to avoid the possibility of injection.
Note 2: Your table design doesn't align with how relational databases are intended to be used. You wouldn't normally have a separate table for each country, you would normally have a country column which allows you to segment the table by country. No good design should end up relying on dynamic SQL, sure you might need it for some edge cases but not your main business flow.
I think that you want to gather the data from the country-specific table and then loop through the country-specific data. I would take the approach of using a "temp" table so that you can insert data from a dynamic SQL statement. Here's what I mean:
create procedure my_looping_procedure as
begin
create table #MyTable (id int)
declare #COUNTER int, #Country varchar(3), #MyTable varchar(50), #sql varchar(100)
SET #COUNTER = 1
set #Country = 'US'
set #MyTable = concat('my_table_', #Country, '_NA')
set #sql = 'insert #MyTable (id) select * from ' + #MyTable
exec(#sql)
WHILE (#COUNTER<= (select max(id) from #MyTable))
BEGIN
SET #COUNTER = #COUNTER + 1
print #COUNTER
end
end
go
exec my_looping_procedure
I eventually resolved the issue by declaring the expression inside the while loop as a text string, like so: set #WhileExpr = concat('#COUNTER <= (select max(id) from ', #MyTable) then using it inside the while parenthesis WHILE (' + #WhileExpr + '))
I apologize for wasting your time.

How to fetch the code comments from a stored procedure / function and populate to a table?

How to fetch the code comments from a stored procedure / function and populate to a table?
/*
Author : Test
Comment : Test
*/
I am working on a user defined function by passing either a stored procedure or function as input parameter to read the code history comments and store it in a table. Having the detail in a table to maintain the version notes for the input.
Check this, there are different ways to get the definition, I prefer sp_helptext because it's already splitted in lines
DECLARE #Objects TABLE(name varchar(100))
DECLARE #Lines TABLE(id int identity, line varchar(maX))
INSERT #Objects
SELECT name FROM sys.objects WHERE Type in ('FN', 'IF', 'P', 'TR', 'TF')
DECLARE #ObjectName VARCHAR(100)
WHILE EXISTS (SELECT 1 FROM #Objects)
BEGIN
SELECT TOP 1 #ObjectName = name FROM #Objects
DELETE #Lines
INSERT #Lines (line)
exec sp_helptext #ObjectName
DECLARE #Linestart INT, #LineEnd INT
WHILE EXISTS(SELECT 1 FROM #Lines WHERE charindex('/*', line) > 0)
BEGIN
SELECT TOP 1 #Linestart = id
FROM #Lines WHERE charindex('/*', line) > 0
ORDER BY id
SELECT TOP 1 #LineEnd = id
FROM #Lines WHERE charindex('*/', line) > 0
ORDER BY id
DECLARE #comment VARCHAR(MAX) = ''
SELECT #Coment = #coment + char(13) + char(10) + line
FROM #Lines
WHERE id between #LineStart and #lineEnd
INSERT INTO yourtable (#objectName, #Comment)
DELETE #Lines WHERE id between #LineStart and #lineEnd
END
DELETE #Objects WHERE name = #ObjectName
END
You can create a function/stored procedure to achieve this:
CREATE FUNCTION InsertCommentIntoTable
(
#Param1 VARCHAR(200)
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE #str VARCHAR(max)
SELECT #str = definition
FROM sys.sql_modules
WHERE object_id = (OBJECT_ID(N'dbo.CustOrderHist'));
--parse #str string value and do your stuffs: #str has the function and stored procedure codes.
RETURN 0;
END
GO

Pass input parameter as column name to update a column value in stored procedure

I have a stored procedure insertvalue with these three parameters:
#stdrollno int,
#sem int,
#IsCheck int,
#subjectname varchar(100),
#test varchar(100)
I have a table demo where there is table named defaulter where there is a column
[Microprocessor th] varchar(50)
[Structured and Object Oriented Analysis and Design th] varchar(50)
whose value I have to update.
So in my stored procedure insertvalue, I wrote this SQL code:
if(#sem = 5)
BEGIN
BEGIN
declare #sql3 nvarchar(500);
set #sql3 = 'update TEdefaulters
set ['+CAST(#test as nvarchar(100))+'] = ['+CAST(#test as nvarchar(100))+'] + '+CAST(#IsCheck as nvarchar(100))+'
where stdrollno = ' +CAST(#stdrollno as nvarchar(100));
exec sp_executesql #sql3
END
BEGIN
update TEdefaulters
set total_theory = total_theory + CONVERT(INT, #ischeck)
where stdrollno = #stdrollno
END
BEGIN
update TEdefaulters
set total_attendance = total_attendance + CONVERT(INT, #ischeck)
where stdrollno = #stdrollno
END
BEGIN
update TEdefaulters
set theory_percentage = (cast((Select total_theory from TEdefaulters where stdrollno=#stdrollno ) as float) / (cast((Select total_theory from TEdefaulters where stdname='total' ) as float))) * 100
where stdrollno=#stdrollno
END
BEGIN
update TEdefaulters
set attendance_percentage = (cast((Select total_attendance from TEdefaulters where stdrollno=#stdrollno ) as float) /(cast((Select total_attendance from TEdefaulters where stdname='total' )as float))) * 100
where stdrollno=#stdrollno
END
END
but it is not working, it causes an error
The values which work is :
exec inserttheoryattendance 5 , 5 , 1 , 'Microprocessor', 'Microprocessor th'
the value which does not work is
exec inserttheoryattendance 5 , 5 , 1 , 'Structured and Object Oriented Analysis and Design', 'Structured and Object Oriented Analysis and Design th'
Cannot convert nvarchar to int
declare #sql nvarchar(50);
50 characters is too short for your SQL String. Try changing it to nvarchar(500)

How to drop multi temporary table?

How to drop multiple temporary table from SQL Server
Below code give this error :
msg 156, Level 15, State 1, Line 5
Incorrect syntax near the keyword 'drop'.
declare #deptno int = 1
while #deptno > (Select COUNT(*) from tbl_deptseat)+1
Begin
Declare #deptnamevar nvarchar(20) = '##dept'+ cast(#deptno as nvarchar(10))
exec (drop table (#deptnamevar))
End
declare #deptno int = 1
while #deptno < (Select COUNT(*) from tbl_deptseat)+1
Begin
Declare #deptnamevar nvarchar(20) = '##dept'+ cast(#deptno as nvarchar(10))
Declare #dropquery nvarchar(20) = 'drop table '+ #deptnamevar
exec (#dropquery)
set #deptno = #deptno + 1
End
This seems like a very strange way of approaching data processing. I wouldn't recommend putting such logic in table names. Instead, the logic belongs in columns.
But, you want to use dynamic SQL:
declare #deptno int = 1;
declare #sql nvarchar(max);
while #deptno < (Select COUNT(*) from tbl_deptseat)+1
Begin
Declare #deptnamevar nvarchar(20) = '##dept'+ cast(#deptno as nvarchar(10));
set #sql = 'drop table ' + #deptnamevar;
exec(#sql) ;
set #deptno = #deptno + 1;
End;

Writing a Stored procedure to search for matching values in columns with optional Input parameters

Requirement: To write stored Procedure(s) such that the values passed in stored procedures are matched against the values in columns in the table and then arranged with highest to lowest matching number of attributes.Then are inserted in a dynamically created temporary table inside the stored procedure.
Problem:
I have say 15-20 attributes that are matched against to confirm the suggestions made in response to record search. Basically, There is a table that stores Patients information and multiple parameters may be passed into the stored procedure to search through so that a Temporary table is created that suggests records in decreasing order of matching attributes.
To frame the basic structure, I tried with 3 attributes and respective stored procedures to match them which in turn are collectively called from a calling procedure based on the input parameter list which in turn creates the required temporary table.
Here is the SQL code(Just to give the gist of what I have tried so far):
But as a matter of fact it is, I realize this is way too naive to be used in a real time application which may require 80-90% of accuracy.So, what exactly can replace this technique for better efficiency?
Create Procedure NameMatch
(
#Name nvarchar(20),
#PercentContribution nvarchar(4) OUT, #PatientName nvarchar(20) out
)
As
declare #temp int
DECLARE #Query nvarchar(500)
if Exists(select Name from dbo.PatientDetails where Name = #Name)
Begin
set #PatientName = #Name
set #query = 'select * from dbo.PatientDetails where Name =' + #Name
set #temp = 0.1*100
set #PercentContribution = #temp + '%'
Execute(#query)
Return
End
Create Procedure AgeMatch
(
#Name nvarchar(20),
#Age int,
#PercentContribution nvarchar(4) OUT, #PatientName nvarchar(20) out
)
As
declare #temp int
DECLARE #Query nvarchar(500)
if Exists(select Name from dbo.PatientDetails where Name =#Name and Age = + #Age)
Begin
set #PatientName = #Name
set #query = 'select * from dbo.PatientDetails where Name = ' + #Name + ' and Age = '+ #Age
set #temp = 0.2*100
set #PercentContribution = #temp + '%'
Execute(#query)
Return
End
Create Procedure Nationality
(
#Name nvarchar(20),
#Age int,
#Nation nvarchar(10),
#PercentContribution nvarchar(4) OUT, #PatientName nvarchar(20) out
)
As
declare #temp int
DECLARE #Query nvarchar(500)
if Exists(select Name from dbo.PatientDetails where Name = #Name and Age = #Age and Nationality = #Nation )
Begin
set #PatientName = #Name
set #query = 'select * from dbo.PatientDetails where Name = ' + #Name + ' and Age = '+ #Age + ' and Nationality = ' + #Nation
set #temp = 0.3*100
set #PercentContribution = #temp + '%'
Execute(#query)
Return
End
create procedure CallingProcedure
(
#Name nvarchar(20),
#Age int = null,
#Nation nvarchar(10)= null
)
As
declare #PercentMatch nvarchar(4)
Begin
create table #results(PatientName nvarchar(30), PercentMatch nvarchar(4))
if(#Nation IS NOT NULL)
Insert into #results exec Nationality #Nation, #Name output, #PercentMatch output
else if(#Age is not Null)
Insert into #results exec AgeMatch #Age, #Name output, #PercentMatch output
else
Insert into #results exec NameMatch #Name, #Name output, #PercentMatch output
End
Setting aside nuances of stored procedure syntax, given parameters 1-n that if not null should match columns 1-n and the results sorted by highest number of matches first, a dynamic query is not needed - plain SQL can do it.
select *
from patient
where #param1 is null or column1 = #param1
or #param2 is null or column2 = #param2
...
or #paramN is null or columnN = #paramN
order by if(column1 = #param1, 1, 0)
+ if(column2 = #param2, 1, 0)
...
+ if(columnN = #paramN, 1, 0) desc