How to create dynamic SQL queries inside CURSOR - sql-server

I have to dynamically create a query inside cursor
DECLARE #id VARCHAR(10)
declare #loc varchar(25)
set #loc = '/MainItem/SubItem';
declare #query varchar(max)
DECLARE myCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT * FROM #tempcolumnname
OPEN myCursor
FETCH NEXT FROM myCursor INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
set #query = 'SELECT * FROM OPENXML(#hdoc, '+#loc+', 3) WITH (code_db_key int)'
exec (#query)
FETCH NEXT FROM myCursor INTO #id
END
but executing this throws an exception
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "#hdoc"
Msg 319, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'with'.
If this statement is a common table expression, an xmlnamespaces clause
or a change tracking context clause, the previous statement
must be terminated with a semicolon.`
But when I am executing the same query outside the cursor, it is working fine.

In cursor you have to again execute your xml file , with xml output declaration.
DECLARE #id VARCHAR(25)
declare #loc varchar(25)
set #loc = '/MainItem/SubItem';
declare #query varchar(max)
DECLARE myCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT * FROM #tempcolumnname
OPEN myCursor
FETCH NEXT FROM myCursor INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
set #query = 'DECLARE #hdoc INT;
EXEC sp_xml_preparedocument #hdoc OUTPUT,'''+ #info+'''
Select Statement
Insert Statement exec (#query)
FETCH NEXT FROM myCursor INTO #id
END
CLOSE myCursor
DEALLOCATE myCursor

Try this :
DECLARE #ParmDefinition nvarchar(500);
/* Build the SQL string one time.*/
set #query =
'SELECT * FROM OPENXML(#Temphdoc, '''+#loc+''', 3) WITH (code_db_key int)';
SET #ParmDefinition = N'#Temphdoc varchar(1000)';
/* This can be in cursor loop */
EXECUTE sp_executesql #query, #ParmDefinition,
#Temphdoc = #hdoc;

Change
set #query = 'SELECT * FROM OPENXML(#hdoc, '+#loc+', 3) WITH (code_db_key int)'
to
set #query = 'SELECT * FROM OPENXML(#hdoc, '+#loc+', 3) WITH (code_db_key int);'
--
Ok, try this,
set #query = CONCAT('SELECT * FROM OPENXML(#hdoc,',+#loc+,', 3) WITH (code_db_key int);')

Related

Parameterized dynamic query within cursor causes ERROR "Procedure expects parameter '#params' of type 'ntext/nchar/nvarchar'"

I need to get the max edit date for each table in our database and store in a temp table. The cursor works fine but when I run exec sp_executesql #sql I get a parameter expectation error:
Parameterized dynamic query within Cursor gives ERROR Procedure expects parameter '#params' of type 'ntext/nchar/nvarchar'
What am I doing wrong?
SET NOCOUNT ON
IF OBJECT_ID('tempdb..##GetMaxVistaEditDate') IS NOT NULL
DROP TABLE ##GetMaxVistaEditDate
CREATE TABLE ##GetMaxVistaEditDate
(
MySchema nvarchar(max),
MyTable nvarchar(max),
MaxVistaEditDate DateTime
)
-- SELECT * FROM ##GetMaxVistaEditDate
DECLARE MyCursor CURSOR FOR
SELECT
SCHEMA_NAME(t.schema_id) Schemaname,
t.name AS TableName
FROM
sys.tables t
WHERE
Schema_Name(t.Schema_id) like 'R_PERS%'
OPEN MyCursor
DECLARE #Schema VARCHAR(100), #Table VARCHAR(100), #MaxVistaEditDate DATETIME
DECLARE #sql NVARCHAR(MAX) = '', #params NVARCHAR(MAX);
SET #params = N'#MaxVistaEditDate DateTime OUTPUT';
FETCH FROM MyCursor INTO #Schema, #Table
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'DECLARE #MaxVistaEditDate DATETIME SELECT #MaxVistaEditDate = (SELECT MAX(VistaEditDate) FROM ' + #SCHEMA + '.' + #TABLE + ')'
EXEC sp_executesql #sql, #MaxVistaEditDate OUTPUT
-- PRINT #SQL
-- PRINT #MaxVistaEditDate
INSERT INTO ##GetMaxVistaEditDate
SELECT #Schema, #Table, #MaxVistaEditDate
FETCH FROM MyCursor INTO #Schema, #Table
END
CLOSE MyCursor
DEALLOCATE MyCursor
You don't have to declare the variables on the sql string, you have to do it on a different variable, and you already have one for that (you name it #params).
Change your #sql definition for the following
SET #SQL = 'Select #MaxVistaEditDate = (SELECT MAX(VistaEditDate) From ' + #SCHEMA + '.' + #TABLE + ')'
And change your call for this:
exec sp_executesql #sql ,#params, #MaxVistaEditDate = #MaxVistaEditDate OUTPUT
and it should work.
Note: Don't forget to close and deallocate the cursor.
you can find an answer in this post
SP_EXECUTESQL and Output Parameter
and your sp_executesql statement don't have parameter definition and you don't have to declare a variable inside the dynamic query
declare #MaxVistaEditDate datetime
exec sp_executesql #sql ,N'#MaxVistaEditDateOut datetime OutPut, #MaxVistaEditDateOut=#MaxVistaEditDate OUTPUT

Cursorfetch: The number of variables declared in the INTO list must match that of selected columns in SQL Server 2012

I created a stored procedure that validates certain columns given a table as parameter and decided to use Cursor. But I am getting this cursor fetch error when trying to execute the stored procedure.
I already double checked the columns and it matches the number of variables in INTO list. I tried modifying the stored procedure by writing this
SELECT Lot, ItemId, PO, Status, ErrorDetails
FROM Table1
instead of
SELECT #SQLSTATEMENT
after
SET #MyCursor = CURSOR FOR
and it works fine. But I want the source table to be a parameter so this won't work for me. Any ideas?
CREATE PROCEDURE [dbo].[ValidateData]
#TABLENAME_PARAM NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #MyCursor CURSOR;
DECLARE #CustomerLot NVARCHAR(100),
#DeviceName NVARCHAR(100),
#PO NVARCHAR(100),
#Status NVARCHAR(1),
#ErrorDetails NVARCHAR(250);
DECLARE #TABLENAME NVARCHAR(100);
DECLARE #SQLSTATEMENT AS NVARCHAR(MAX);
SELECT #TABLENAME = Quotename (TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #TABLENAME_PARAM
SET #SQLSTATEMENT = 'Select Lot, ItemId, PO, Status, ErrorDetails FROM ' + #TABLENAME + ' WHERE Status = ''N'''
BEGIN
SET #MyCursor = CURSOR FOR
SELECT #SQLSTATEMENT
OPEN #MyCursor
FETCH NEXT FROM #MyCursor INTO #CustomerLot, #DeviceName, #PO, #Status, #ErrorDetails
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRAN
--some validations here
COMMIT TRAN
FETCH NEXT FROM #MyCursor INTO #CustomerLot, #DeviceName, #PO, #Status, #ErrorDetails
END;
CLOSE #MyCursor;
DEALLOCATE #MyCursor;
END
END
GO
Try this-
CREATE PROCEDURE [dbo].[ValidateData] #TABLENAME_PARAM NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
--DECLARE #MyCursor CURSOR;
DECLARE
#CustomerLot NVARCHAR(100),
#DeviceName NVARCHAR(100),
#PO NVARCHAR(100),
#Status NVARCHAR(1),
#ErrorDetails NVARCHAR(250);
DECLARE #TABLENAME NVARCHAR(100);
DECLARE #SQLSTATEMENT AS NVARCHAR(MAX);
SELECT #TABLENAME = Quotename (TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #TABLENAME_PARAM
SET #SQLSTATEMENT = 'DECLARE MyCursor CURSOR FOR Select Lot, ItemId, PO, Status, ErrorDetails FROM ' + #TABLENAME + ' WHERE Status = ''N'''
EXEC sp_executesql #sqlstatement
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO #CustomerLot, #DeviceName, #PO, #Status, #ErrorDetails
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRAN
--some validations here
COMMIT TRAN
FETCH NEXT FROM MyCursor INTO #CustomerLot, #DeviceName, #PO, #Status, #ErrorDetails
END;
CLOSE MyCursor;
DEALLOCATE MyCursor
END
END
GO

How to dynamically declare a cursor in T-SQL?

I'm trying to use a dynamic query to declare a cursor. Basically I have the name of the table-valued function I will use for the cursor as a column of a table so I must declare the cursor using a SQL statement.
The problem is that T-SQL doesn't recognize myCursor as a valid cursor.
DECLARE #ColumnA nvarchar(250)
DECLARE #ColumnB nvarchar(250)
DECLARE #FunctionName nvarchar(250)
DECLARE #RecordId nvarchar(250)
DECLARE #sqlStatement nvarchar(MAX)
SET #sqlStatement = 'DECLARE myCursor CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT * FROM ' + #FunctionName + '(''' + #RecordId + ''')'
EXEC sp_executesql #sqlStatement
OPEN myCursor
FETCH NEXT FROM myCursor INTO #ColumnA, #ColumnB
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ColumnA, #ColumnB
FETCH NEXT FROM myCursor INTO #ColumnA, #ColumnB
END
CLOSE myCursor
DEALLOCATE myCursor
Any help or workarounds are welcome.
EDIT: I've solved the problem by declaring the cursor before and using output from sqlstatement to pass values.
DECLARE #myCursor CURSOR
SET #sqlStatement = 'SET #myCursor = CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT * FROM ' + #FunctionName + '(''' + #RecordId + ''');OPEN #myCursor;'
EXEC sp_executesql #sqlStatement, N'#Placeholder nvarchar(250), #Placeholdervalue nvarchar(250), #myCursor CURSOR OUTPUT', #Placeholder = #Placeholder, #Placeholdervalue = #Placeholdervalue, #myCursor = #myCursor OUTPUT
It is not the best solution but if you really have to do this - an easier way is to use a dynamic SQL to load the data you need into a global temp table first. The dynamic SQL statement is much simpler i.e.:
SET #sqlStatement = 'SELECT * INTO ##MyGlobalTemp FROM ' + #FunctionName + '(''' + #RecordId + ''')'
EXEC sp_executesql #sqlStatement
Then use a regular (non-dynamic) cursor to loop through the ##MyGlobalTemp table (and don't forgot to drop the temp after you are finished).
i.e.:
DECLARE myCursor CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT column1, column2, etc.. FROM ##MyGlobalTemp...... etc..
Dynamic cursors could get hairy. Just make sure the temp has a global (##) scope.
Hope this helps.

T-SQL cursor for linked server INSERT SELECT

I'm trying to get this cursor loop to work, so I could copy data from linked server to another server. However it seems that the cursor is in a loop and does nothing. What am I doing wrong?
/* For testing purposes I'm fetching data from
1 company only. The result should be one row.*/
DECLARE #tmp_key VARCHAR(14)
DECLARE #db cursor
DECLARE #sql NVARCHAR(MAX)
SET #db = CURSOR FOR
SELECT [CompanyId] FROM [Test].[dbo].[Company] WHERE [CompanyId] = '0001'
SET #sql = N'INSERT INTO [Stagingarea].[dbo].[Cominfo]
SELECT
convert(nvarchar(100),[Nro])
,convert(nvarchar(100),'+#tmp_key+' )
FROM [Linked_server_name].TK'+#tmp_key+'.[dbo].[cominfo]
where [rule1] <> 0 and acc = 1777';
OPEN #db
FETCH NEXT FROM #db INTO #tmp_key
while (##fetch_status = 0)
begin
EXEC sp_sqlexec #sql
end;
CLOSE #db
DEALLOCATE #db
You should put the dynamic SQL inside the WHILE loop. Make sure to call FETCH NEXT inside to avoid infinite loop.
DECLARE #tmp_key VARCHAR(14)
DECLARE #db cursor
DECLARE #sql NVARCHAR(MAX)
SET #db = CURSOR FOR
SELECT [CompanyId] FROM [Test].[dbo].[Company] WHERE [CompanyId] = '0001'
OPEN #db
FETCH NEXT FROM #db INTO #tmp_key
WHILE(##FETCH_STATUS = 0) BEGIN
SET #sql = N'INSERT INTO [Stagingarea].[dbo].[Cominfo]
SELECT
CONVERT(NVARCHAR(100), [Nro])
,CONVERT(NVARCHAR(100),' + #tmp_key +')
FROM [Linked_server_name].TK' + #tmp_key + '.[dbo].[cominfo]
WHERE [rule1] <> 0 AND acc = 1777';
EXEC sp_sqlexec #sql
FETCH NEXT FROM #db INTO #tmp_key
END;
CLOSE #db
DEALLOCATE #db

Error in Declaration of cursor

set #SQL=N' select #minTableId = MIN(id) from ' + #AcDB + '.dbo.vTblOfRollNo '
Declare Cursor For
EXEC SP_EXECUTESQL #SQL
if i have declared all the variables in above query but Declaration of cursor in above
query shows ERROR.
What is Solution?
In order to execute a cursor over dynamic SQL you must put the output of your dynamic sql into a temporary table and then cursor over the temporary table like this:
DECLARE #TableName NVARCHAR(100)
DECLARE #SQL NVARCHAR(1000)
CREATE TABLE #TempTABLE(email NVARCHAR(200))
SET #TableName='Users'
SELECT #SQL='INSERT INTO #TempTable SELECT email FROM ' + #TableName
EXEC (#SQL)
DECLARE MyCursor CURSOR FOR SELECT * FROM #TempTable
OPEN MyCursor
DECLARE #Email NVARCHAR(200)
FETCH NEXT FROM MyCursor INTO #Email
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'Email = ' + #Email
FETCH NEXT FROM MyCursor INTO #Email
END
CLOSE MyCursor
DEALLOCATE MyCursor
DROP TABLE #TempTABLE
I dont think you need a cursor for this
try
DECLARE #AcDB VARCHAR(10),
#Sql NVARCHAR(MAX)
set #SQL=N' select MIN(id) from ' + #AcDB + '.dbo.vTblOfRollNo '
DECLARE #Temp TABLE(
MinID INT
)
INSERT INTO #Temp EXEC SP_EXECUTESQL #SQL
DECLARE #minTableId INT
SELECT TOP 1 #minTableId = MinID FROM #Temp
SELECT #minTableId
EDIT: Also here is the actual CURSOR documentation
DECLARE CURSOR (Transact-SQL)

Resources