SQL cursor variable can not use in conditions - sql

any idea why my #ProductNumber cursor variable is not working if used in dynamic statement? It says "Must declare scalar variable". But this is a cursor variable. But when I used it not dynamic, it works. Thanks.
DECLARE #TargetDB NVARCHAR(50)
DECLARE #SourceDB NVARCHAR(50)
DECLARE #DateEffectiveFrom datetime
DECLARE #InsertRecords NVARCHAR(1000)
SET #TargetDB = 'MySSISDb'
SET #SourceDB = 'MySSISDb2'
SET #DateEffectiveFrom = '2013-12-29'
Declare #ProductNumber INT;
DECLARE #SqlDb NVARCHAR(Max)
DECLARE Cur1 CURSOR FOR SELECT CAST(#SqlDb AS NVARCHAR(MAX))
SET #SqlDb = 'SELECT ProductNumber From '+#SourceDB+'.dbo.Item (NOLOCK)
WHERE NOT EXISTS (SELECT 1 FROM '+#TargetDB+'.dbo.Item2
WHERE ProductNumber = '+#SourceDB+'.dbo.Item.ProductNumber)'
EXEC sp_executesql #SqlDb
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #ProductNumber;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #InsertRecords = 'INSERT INTO ' + #TargetDB + '.dbo.Item2(
ProductNumber, ProductName, ListPrice, BirthDate)
SELECT ProductNumber, ProductName,
ListPrice, ''' + CONVERT(nvarchar(25),#DateEffectiveFrom,120) + '''
FROM ' + #SourceDB + '.dbo.Item
WHERE ' + #SourceDB + '.dbo.Item.ProductNumber = #ProductNumber'
--WHERE ' + #SourceDB + '.dbo.Item.ProductNumber = #ProductNumber
--(this is where the problem)
EXEC sp_executesql #InsetRecords
FETCH NEXT FROM Cur1 INTO #ProductNumber;
END
CLOSE Cur1;
DEALLOCATE Cur1;

Shift the declaration of your cursor into the SQLDb variable. In SQL Server, at least, the cursor designation "Cur1" is accessible after the execution of #SQLDb. Also, I've moved the use of #ProductNumber to outside the literal string and fixed a typo on your second EXEC statement within the cursor while loop (#InsetRecords to #InsertRecords).
DECLARE #TargetDB NVARCHAR(50)
DECLARE #SourceDB NVARCHAR(50)
DECLARE #DateEffectiveFrom datetime
DECLARE #InsertRecords NVARCHAR(1000)
SET #TargetDB = 'MySSISDb'
SET #SourceDB = 'MySSISDb2'
SET #DateEffectiveFrom = '2013-12-29'
Declare #ProductNumber INT;
DECLARE #SqlDb NVARCHAR(Max)
SET #SqlDb = '
DECLARE Cur1 CURSOR FOR
SELECT ProductNumber
From '+#SourceDB+'.dbo.Item (NOLOCK)
WHERE NOT EXISTS (
SELECT 1
FROM '+#TargetDB+'.dbo.Item2
WHERE ProductNumber = '+#SourceDB+'.dbo.Item.ProductNumber)'
EXEC sp_executesql #SqlDb
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #ProductNumber;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #InsertRecords = 'INSERT INTO ' + #TargetDB + '.dbo.Item2(
ProductNumber, ProductName, ListPrice, BirthDate)
SELECT ProductNumber, ProductName,
ListPrice, ''' + CONVERT(nvarchar(25),#DateEffectiveFrom,120) + '''
FROM ' + #SourceDB + '.dbo.Item
WHERE ' + #SourceDB + '.dbo.Item.ProductNumber = ' + CONVERT(nvarchar(max), #ProductNumber)
EXEC sp_executesql #InsertRecords
FETCH NEXT FROM Cur1 INTO #ProductNumber;
END
CLOSE Cur1;
DEALLOCATE Cur1;

Related

Create tables based on the rows value in a table in sql

My requirement is some thing like this
I have a table tblDetails which contains all the details about the table
TblCol TblName ColNo ColName Type Length Sacle KeyType description
Now based on values in tblDetails I want to create the tables.
Here is a (very) basic example of how to approach this sort of dynamic SQL. This example does not add indexes, keys, or anything fancy - just some tables and columns with datatypes, but it should get you started in the right direction:
DECLARE #sql NVARCHAR(MAX)
DECLARE #tblName nvarchar(255)
DECLARE tbl_cursor CURSOR LOCAL FOR
SELECT DISTINCT tblName
FROM tblDetails
ORDER BY tblName
OPEN tbl_cursor
FETCH NEXT FROM tbl_cursor INTO #tblName
--OUTER LOOP THROUGH TABLES:
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'CREATE TABLE ' + #tblName + '( ';
DECLARE #colName nvarchar(255),
#type nvarchar(50),
#length nvarchar(50),
#ctr int = 0;
DECLARE col_cursor CURSOR LOCAL FOR
SELECT [colName], [Type], [Length]
FROM tblDetails
WHERE tblName = #tblName
ORDER BY ColNo
OPEN col_cursor
FETCH NEXT FROM col_cursor INTO #colName, #type, #length
--INNER LOOP THROUGH COLUMNS
WHILE ##FETCH_STATUS = 0
BEGIN
IF #ctr != 0 --if this is not the first column, prefix w/ comma
SET #sql += ', ';
SET #sql += '[' + #colName + '] ' + #type;
IF #type IN ('CHAR','VARCHAR','NCHAR','NVARCHAR','BINARY','VARBINARY') --add size if appropriate
SET #sql += '(' + #length + ')'
SET #ctr += 1;
FETCH NEXT FROM col_cursor INTO #colName, #type, #length
END --inner loop
CLOSE col_cursor
SET #sql += ')';
EXECUTE sp_executesql #sql
FETCH NEXT FROM tbl_cursor INTO #tblName
END --outer loop
CLOSE tbl_cursor

Dynamic SQL and loop challenge, SQL Server 2012

I have n tables that is unknown before runtime with tablename always being tablename1, tablename2... tablenameN. The first column of each table is always Name. The challenge is to change that column name in each table to Name1, Name2.. NameN. I know I should be using sp_rename and a loop. Having trouble building up the query, I'm pretty new to SQL. Help would be appreciated. THanks
This should do the rename:
DECLARE #counter INT;
DECLARE #tableName NVARCHAR(100);
DECLARE #columnName NVARCHAR(100);
DECLARE #newColumnName NVARCHAR(100);
SET #counter = 1;
WHILE #counter < 65536
BEGIN
SET #tableName = 'tableName' + CAST(#counter AS NVARCHAR)
IF EXISTS(SELECT * FROM sys.tables WHERE name = #tableName)
BEGIN
SET #columnName = #tableName + N'.name';
SET #newColumnName = N'name' + CAST(#counter AS NVARCHAR);
EXEC sp_rename #objname=#columnName, #newName=#newColumnName;
END
ELSE
BEGIN
SET #counter = 65536
END
SET #counter = #counter + 1
END
It's a bit crude though.. and renames only 65535 tables and full amount only when there's none missing in between.
Uncomment sql_exec when you're sure it does what you're expecting :)
DECLARE #TableName sysname, #ColName sysname
DECLARE #num sysname
DECLARE #sql nvarchar(4000)
DECLARE cTables CURSOR FOR SELECT name from dbo.sysobjects where Category = 0 AND type NOT IN (N'F', N'FN', N'IF', N'TF', N'P', N'TR', N'V', N'K') AND name like 'tablename%'
OPEN cTables
FETCH NEXT FROM cTables INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #num = SUBSTRING(#Tablename, 10, 5)
SET #sql = N'sp_RENAME ''' + #TableName + '.[Name]'' , ''[Name' + #num + ']'', ''COLUMN'''
PRINT #sql
-- EXEC sp_sqlexec #sql
FETCH NEXT FROM cTables INTO #TableName
END
CLOSE cTables;
DEALLOCATE cTables;
Here's a SP - give it a try ;-)
CREATE PROCEDURE dbo.Rename
(
#n INT
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #Stmt NVARCHAR(MAX)
DECLARE #i INT
DECLARE #tabname NVARCHAR(MAX)
DECLARE #colname NVARCHAR(MAX)
SET #i = 1
WHILE #i <= #n
BEGIN
SET #tabname = N'tablename' + CAST(#i AS NVARCHAR(MAX))
SET #colname = N'name' + CAST(#i AS NVARCHAR(MAX))
IF EXISTS(SELECT TOP 1 1 FROM sys.tables t WHERE t.name = #tabname)
BEGIN
SET #Stmt = N'EXEC sp_rename ''' + #tabname + '.[name]'', ''' + #colname +''',''COLUMN'''
--PRINT #Stmt
EXEC sp_executesql #Stmt
END
SET #i = #i + 1
END
END

How to Set a variable using OPENQUERY in SQL Server

I am trying to read data from a table. This table have a list of table name.
for each row of the data set I want to run a couple of queries to pull data and insert it into a temporary table.
Here is What I have done
DECLARE #campName varchar(255);
DECLARE #sqlCommand varchar(1000);
DECLARE #sqlCommandMySQL varchar(1000);
DECLARE #LastRun varchar(60);
DECLARE #OPENQUERY varchar(1000);
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT LTRIM(RTRIM(CallListName)) AS CallListName
FROM [SMSQL1].[RDI_System].[dbo].[Campaigns]
WHERE dialer_campaign = 1 AND i3Server ='I3New' AND ClientID = 111 AND (EndDate IS NULL OR EndDate >= getdate() - 7)
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #campName
WHILE ##FETCH_STATUS = 0
BEGIN
--SET #LinkedServer = 'GUARDIAN';
SET #OPENQUERY = 'SELECT #LastRun = lr FROM OPENQUERY(GUARDIAN,''';
SET #sqlCommandMySQL = 'SELECT IFNULL(MAX(lastRun), DATE_SUB(NOW(), INTERVAL 7 DAY) ) AS lr
FROM guardian_dynamo.runtimes_i3
WHERE CampaignListName = "'+#campName+'" '')';
print #OPENQUERY + #sqlCommandMySQL;
EXEC(#OPENQUERY + #sqlCommandMySQL);
SET #sqlCommand = ' INSERT INTO #finalList(Attemtps, CAMPAIGNNAME, FINISHCODE, CALLDATE, AGENTID, RDINotes, PHONE, MERCHANTAccount)
SELECT ATTEMPTS, CAMPAIGNNAME, FINISHCODE, CALLDATE, AGENTID, RDINotes, PHONE, MERCHANTAccount
FROM [I3_IC4].[dbo].['+ #campName +']
WHERE CALLDATE > '''+#LastRun+''' AND ISNULL(status, ''C'') IN (''U'', ''E'', ''A'', ''F'') ';
EXEC (#sqlCommand);
FETCH NEXT FROM MY_CURSOR INTO #campName
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR;
every time I run this query I get this error
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#LastRun".
I am not sure why since I am declaring this variable on the top as you can see in my code above.
the took the output of print #OPENQUERY + #sqlCommandMySQL; and executed that manually. It worked with no issue and the variable #LastRun will have a datetime value as it should.
You need to use sp_executesql to execute the dynamic query which helps you output the variable(#LastRun)
Declare #OPENQUERY Nvarchar(max), #sqlCommandMySQL Nvarchar(max), #OPENQUERYFINAL Nvarchar(max)
....
SET #OPENQUERY = 'SELECT #LastRun = lr FROM OPENQUERY(GUARDIAN,''';
SET #sqlCommandMySQL = 'SELECT IFNULL(MAX(lastRun), DATE_SUB(NOW(), INTERVAL 7 DAY) ) AS lr
FROM guardian_dynamo.runtimes_i3
WHERE CampaignListName = "'+#campName+'" '')';
--print #OPENQUERY + #sqlCommandMySQL;
SET #OPENQUERYFINAL = #OPENQUERY + #sqlCommandMySQL;
EXEC sp_executesql #OPENQUERYFINAL,
N'#LastRun varchar(10) OUTPUT',
#LastRun output
Demo
DECLARE #str VARCHAR(10),
#sql NVARCHAR(max)
SET #sql= 'select #str=1 '
EXEC Sp_executesql
#sql,
N'#str varchar(10) OUTPUT',
#str output
PRINT #str

The number of variables declared in the INTO list must match that of selected columns one select one into

This is a pretty straightforward error, but I can't figure out why I am getting it. I have one column selected in my declare (TABLE_NAME) and am fetching into one variable (#cTableName). What gives?
CREATE PROCEDURE [dbo].[updateNumbers_ArchiveDB]
(
#accountNumber varchar(50),
#padding varchar(50),
#proc_dateStart datetime,
#proc_dateEnd datetime
)
AS
DECLARE #cTableName varchar(50)
DECLARE CursorYearlyTables CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME like 'Archive[12]%' and len(TABLE_NAME) = 14
ORDER BY TABLE_NAME;
-- =============================================
-- Open the cursor and iterate over the monthlies
-- =============================================
OPEN CursorYearlyTables
fetch next from CursorYearlyTables into #cTableName
while (##fetch_status <> -1)
BEGIN
SET NOCOUNT ON;
declare #q varchar(1000);
set #q = 'Update' + #cTableName +
'SET LogicalAccount = #padding + #accountNumber' +
'WHERE ProcessDate BETWEEN CAST(#proc_dateStart AS DATE) AND CAST(#proc_dateEnd AS DATE)'
exec(#q)
fetch next from CursorYearlyTables into #cTableName
END
close CursorYearlyTables;
DEALLOCATE CursorYearlyTables;
Could you try it with these lines in the cursor
declare #q nvarchar(max);
set #q = 'Update ' + #cTableName +
'SET LogicalAccount = '+#padding + #accountNumber +
'WHERE ProcessDate BETWEEN CAST('''+CONVERT(VARCHAR(20),#proc_dateStart)+''' AS DATE) AND CAST('''+CONVERT(VARCHAR(20),#proc_dateEnd)+''' AS DATE)'
exec sp_executesql #q
to account for SQL_Injection DavidG commented:
declare
#q nvarchar(max) = 'update '+#cTableName+' SET LogicalAccount = #a where ProcessDate BETWEEN CAST(#b AS DATE) AND CAST(#c AS DATE))',
#param1 nvarchar(100) = #padding+ #accountNumber,
#ParamDefinition nvarchar(500) = N'#a varchar(100), #b datetime, #c datetime'
exec sp_executesql #q, #ParamDefinition,#a = #param1,#b = #proc_dateStart, #cTableName = #proc_dateEnd
Please use CONVERT function
CAST('''+CONVERT(VARCHAR(108),#proc_dateStart)+''' AS DATE) AND CAST('''+CONVERT(VARCHAR(108),#proc_dateEnd)+''' AS DATE)'

SQL Cursor within Stored Procedure to populate string variable

I have a stored procedure that contains a cursor to loop through SQL records and populates the string which I will use later as my email text. I'm trying to print it out to verify before I can proceed with it but it seems to not populate the string. Here is my stored procedure in SQL Server 2005.
CREATE PROCEDURE [dbo].[spBody]
AS
DECLARE #MyCursor CURSOR
DECLARE #emailBody nvarchar(max)
DECLARE #statusName nvarchar(max)
DECLARE #deptCode nvarchar(max)
DECLARE #instructors nvarchar(max)
DECLARE #meetingTime nvarchar(max)
SET #MyCursor = CURSOR FAST_FORWARD For
Select StatusName, DeptCode, Instructors, Description from MyTable where StatusID = (select CAST(value AS INT) from Table2 where ConfigOption = 'RequiredStatus')
Open #MyCursor
FETCH NEXT FROM #MyCursor INTO #statusName, #deptCode, #instructors, #meetingTime
WHILE ##FETCH_STATUS = 0
BEGIN
SET #emailBody = #emailBody + #statusName + ' ' + #deptCode + ' ' + #instructors + ' ' + #meetingTime
FETCH NEXT FROM #MyCursor INTO #statusName, #deptCode, #instructors, #meetingTime
END
CLOSE #MyCursor
Print #emailBody
DEALLOCATE #MyCursor
It's because #emailBody starts out as NULL, and any concatenation with NULL yields NULL by default. Do a
SET #emailBody = '';
at the beginning of your script.
Also, strongly consider adding a SET NOCOUNT ON; statement at the top of your stored procedure -- not having NOCOUNT ON can greatly slow the execution of your proc.
Why do you need a cursor for this string concat. Wont the following query suffix
DECLARE #emailBody nvarchar(max)
Set #emailBody = ''
Select #emailBody = #emailBody + StatusName + ' ' + DeptCode + ' ' + Instructors + ' ' + [Description] from MyTable where StatusID = (select CAST(value AS INT) from Table2 where ConfigOption = 'RequiredStatus')
Print #emailBody