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.
Related
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
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
I'm trying to iterate over some tables and clear all records.
My code is the following :
DECLARE #table varchar(100)
DECLARE db_cursor CURSOR FOR select name from sys.tables where name like '%cfe_%'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
print #table
delete from #table
FETCH NEXT FROM db_cursor INTO #table
END
CLOSE db_cursor
DEALLOCATE db_cursor
But I receive "Must declare the table variable "#table" at the line "delete..."
I can't see the error.
Thank you
You shoud use dynamic query,
DECLARE #table varchar(100)
,#v_str nvarchar(200)
DECLARE db_cursor CURSOR FOR select name from sys.tables where name like '%cfe_%'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
print #table
set #v_str = 'delete from '+#table
exec(#v_str)
FETCH NEXT FROM db_cursor INTO #table
END
CLOSE db_cursor
DEALLOCATE db_cursor
You need dynamic delete statement... Try this :
DECLARE #cmd VARCHAR(4000)
DECLARE #table varchar(100)
DECLARE db_cursor CURSOR FOR select name from sys.tables where name like '%cfe_%'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = 'DELETE FROM '+#table
EXEC (#cmd)
FETCH NEXT FROM db_cursor INTO #table
END
CLOSE db_cursor
DEALLOCATE db_cursor
Even better would be to not use a cursor here. Looping in sql is a last resort. Also, your query is not going to do exactly what you think it will because you are using like and wanting to find an underscore. The underscore in a LIKE predicate requires it to be escaped with square brackets. As posted your query will return any table with cfe in the name not cfe_.
Once you are comfortable that the dynamic sql string is what you want you can uncomment it to execute it.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'delete from ' + name + ';'
from sys.tables
where name like '%cfe[_]%'
select #SQL
--exec sp_executesql #SQL
We can also use while loop for this process
DECLARE #Min Int,#max Int
IF Object_id('Tempdb..#DeleteList')IS NOT NULL
DROP TABLE #DeleteList
CREATE TABLE #DeleteList (Id Int Identity,Name varchar(100))
INSERT INTO #DeleteList(Name)
SELECT name FROM sys.tables WHERE CHARINDEX('cfe_',name)>0
SELECT #Min=Min(Id) FROm #DeleteList
SELECT #max=MAX(Id) FROm #DeleteList
While(#Min<=#max)
Begin
Declare #TableName Varchar(50),
#Sql Nvarchar(max)
SELECT #TableName=Name From #DeleteList Where id=#Min
SET #Sql='DELETE From '+#TableName
Exec (#Sql)
SET #Min=#Min+1
END
But if the deleting tables have foreign key refrences it will throw error so that first you need delete records from child and then go to Parent table
You will need to do something like;
EXEC sp_executesql #statement = N'DELETE FROM ' + #table
because currently you are trying to delete from a String variable, not the table named the same as the variable
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);')
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)