T-SQL Cursor never ends - sql-server

I already searched with Google but I didn't find answer to my question. In the internet everything says that I do it correct.
My problem is - I am trying for now write values what I saved to cursor (yes, only that). However loop which I use for it is infinity and it writes all values from cursor from start to end, after again and again and again ...
CREATE PROCEDURE CreateOrEditClient(...Parameters...) AS
DECLARE c CURSOR FOR SELECT column FROM table;
DECLARE #wrt VARCHAR(20);
DECLARE #tmp INT = 0;
BEGIN
OPEN c;
FETCH NEXT FROM c INTO #wrt;
WHILE ##FETCH_STATUS = 0
BEGIN
print CAST(#tmp AS VARCHAR(10)) + ' ' + #wrt;
SET #tmp = #tmp +1;
FETCH NEXT FROM c INTO #wrt;
END;
CLOSE c;
DEALLOCATE c;
END;
EXEC CreateOrEditClient ...;
In my opinion cursor is written correct but output is:
0 790710/1112
1 900519/5555
2 790716/7877
....
19 111111/1111
0 790710/1112
1 900519/5555
....
and in the end it writes error message "Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32)."
If I use only
SELECT column FROM table;
It writes only 20 records.
I know there is better solution for this example but I need to know why the Cursor doesn't work. It may be useful in future. Thanks for every asnwer.

Ok, in comments under question is solution.
"EXEC ProcedureName ...;" can't be in the same file like procedure's body when you compile.
CREATE PROCEDURE CreateOrEditClient(...Parameters...) AS
DECLARE c CURSOR FOR SELECT column FROM table;
DECLARE #wrt VARCHAR(20);
DECLARE #tmp INT = 0;
BEGIN
OPEN c;
FETCH NEXT FROM c INTO #wrt;
WHILE ##FETCH_STATUS = 0
BEGIN
print CAST(#tmp AS VARCHAR(10)) + ' ' + #wrt;
SET #tmp = #tmp +1;
FETCH NEXT FROM c INTO #wrt;
END;
CLOSE c;
DEALLOCATE c;
END;
The example in question will call procedure recursively.
Thanks to all.

Please Try this ...
BEGIN
SET NOCOUNT ON ;
DECLARE #wrt VARCHAR(20)
DECLARE #tmp INT = 0
DECLARE Cur_Common CURSOR FAST_FORWARD READ_ONLY FOR
SELECT iID FROM 'YourTable'
OPEN Cur_Common
FETCH NEXT FROM Cur_Common INTO #wrt
WHILE ##FETCH_STATUS = 0
BEGIN
print CAST(#tmp AS VARCHAR(10)) + ' ' + #wrt;
SET #tmp = #tmp +1;
FETCH NEXT FROM Cur_Common INTO #wrt
END
CLOSE Cur_Common
DEALLOCATE Cur_Common
END

You would probably notice if you would keep your code indented correctly. You have:
CREATE PROCEDURE CreateOrEditClient(...Parameters...) AS
DECLARE c CURSOR FOR SELECT column FROM table;
DECLARE #wrt VARCHAR(20);
DECLARE #tmp INT = 0;
BEGIN
OPEN c;
FETCH NEXT FROM c INTO #wrt;
WHILE ##FETCH_STATUS = 0
BEGIN
print CAST(#tmp AS VARCHAR(10)) + ' ' + #wrt;
SET #tmp = #tmp +1;
FETCH NEXT FROM c INTO #wrt;
END;
CLOSE c;
DEALLOCATE c;
END;
You should have:
CREATE PROCEDURE CreateOrEditClient(...Parameters...) AS
BEGIN
DECLARE #wrt VARCHAR(20);
DECLARE #tmp INT = 0;
DECLARE c CURSOR FOR SELECT column FROM table;
OPEN c;
FETCH NEXT FROM c INTO #wrt;
WHILE ##FETCH_STATUS = 0
BEGIN
print CAST(#tmp AS VARCHAR(10)) + ' ' + #wrt;
SET #tmp = #tmp +1;
FETCH NEXT FROM c INTO #wrt;
END;
CLOSE c;
DEALLOCATE c;
END;
EXEC CreateOrEditClient ...;
BTW. Did your code even run? Seems like a syntax error to me.
EDIT: Oh, and parameters are not declared in brackets AFAIK. For example:
CREATE PROCEDURE tmp_mol_cleanup_db
#param1 type,
#param2 type
AS
BEGIN
-- procedure body
END;

use
DECLARE #tmp INT
SET #tmp=0
instad of
DECLARE #tmp INT = 0

Related

SQL Server Nested Cursors and Variables Declaration

I have a doubt regarding the variable declaration in a nested cursors scenario.
This is an small nested cursor sample that i found. In other samples I've seen I also find DECLARE clauses inside the first cursor.
DECLARE #ClientID int;
DECLARE Cur1 CURSOR FOR SELECT ClientID From Folder;
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #ClientID;
SELECT #FETCH_Cur1 = ##FETCH_STATUS
WHILE #FETCH_Cur1 = 0
BEGIN
DECLARE #UID int;
DECLARE Cur2 CURSOR FOR SELECT UID FROM Attend Where ClientID=#ClientID;
OPEN Cur2;
FETCH NEXT FROM Cur2 INTO #UID;
SELECT #FETCH_Cur2 = ##FETCH_STATUS
WHILE #FETCH_Cur2 = 0
BEGIN
PRINT 'Found UID: ' + Cast(#UID as Varchar);
FETCH NEXT FROM Cur2 INTO #UID;
SELECT #FETCH_Cur2 = ##FETCH_STATUS
END;
CLOSE Cur2;
DEALLOCATE Cur2;
FETCH NEXT FROM Cur1 INTO #ClientID;
SELECT #FETCH_Cur1 = ##FETCH_STATUS
END;
PRINT 'DONE';
CLOSE Cur1;
DEALLOCATE Cur1;
The code works, but my doubt is if it's correct the DECLARATIONS inside the first cursor.
DECLARE #UID int;
Shouldn't Declarations be placed at the beginning of code, as is normally done for other programming languages?
You can DECLARE a variable inside a WHILE, yes; the latter DECLAREs will simply be ignored. If you declared the variable and assigned it a value at the time (for example DECLARE #UID int = 1; it would be assigned 1 in each iteration:
DECLARE #I int = 1;
WHILE #i < 10 BEGIN
DECLARE #W int;
SET #W = ISNULL(#W,1) + 1;
SET #I = #I + 1
END
SELECT #W; --10
GO
DECLARE #I int = 1;
WHILE #i < 10 BEGIN
DECLARE #W int = 0;
SET #W = ISNULL(#W,1) + 1;
SET #I = #I + 1
END
SELECT #W; -- 1
DB<>fiddle
Of course, I personally prefer to DECLARE the variables outside of the WHILE as I feel the code is "cleaner", but that doesn't mean you have to.

Don't know how to use varchar variable in select clause of query

I''m trying to use a varchar variable in my select, but its registering as a string and outputting just the string itself instead of treating it like an actual column.
Not very many solutions online, as it seems like this isn't a problem very many run into.
Declare #counter INT = 0
Declare #totalcol INT
Declare #col VARCHAR(50)
select #totalcol = count(*)
FROM [Loyalty_DW].information_schema.columns
WHERE table_name = 'Transactions'
while (#counter < #totalcol)
begin
select #col = COLUMN_NAME
from [Loyalty_DW].information_schema.columns
where table_name = 'Transactions'
order by (select null)
offset #counter rows
fetch next 1 rows only
select distinct(#col)
from [Loyalty_DW].dbo.Transactions
set #counter += 1
end
The output is just a string with no actual data returned. The same as if I were to say select 'asdf' from tablename where ... it would just output 'asdf'.
You can use CURSOR statement rather than WHILE statement, as Gordon says you need to use dynamic SQL:
DECLARE #columName AS NVARCHAR(50);
DECLARE #sqlText AS NVARCHAR(1000);
DECLARE cursor_name CURSOR FOR
SELECT COLUMN_NAME
FROM [Loyalty_DW].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Transactions'
ORDER BY(SELECT NULL)
OPEN cursor_name
FETCH NEXT FROM cursor_name INTO #columName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlText = N'SELECT DISTINCT ' + #columName + ' FROM [Loyalty_DW].dbo.Transactions'
EXEC (#sqlText)
FETCH NEXT FROM cursor_name INTO #columName
END
CLOSE cursor_name
DEALLOCATE cursor_name

SQL Server stored procedure to empty specific tables

I have a stored procedure which is supposed to truncate specific tables based on their names.
Tables which do not have "A" as the first character in their names must be truncated.
The problem is that this code doesn't work! Please help
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[return_data]
#DBName varchar(100)
AS
declare #i int, #tc int
set #i = 1
DECLARE #sql NVARCHAR(100)
SET #sql = 'USE ' + QUOTENAME(#DBName) + '
DECLARE cur CURSOR FOR SELECT TABLE_NAME FROM information_schema.tables
'
EXECUTE(#sql)
OPEN cur
declare #tbl_name nvarchar(100)
declare #first_char char(1)
FETCH NEXT FROM cur INTO #tbl_name
WHILE ##FETCH_STATUS = 0 BEGIN
set #first_char = SUBSTRING(#tbl_name, 0, 1)
set #sql = 'DELETE FROM ' + QUOTENAME(#tbl_name)
if (#first_char != 'A')
begin
EXECUTE(#sql)
end
FETCH NEXT FROM cur INTO #tbl_name
END
CLOSE cur
DEALLOCATE cur
return
The problem is with the SUBSTRING function. The starting position is a 1-based ordinal, not 0-based. Try
SUBSTRING(#tbl_name, 1, 1)
or
LEFT(#tbl_name, 1)
Also, I suggest you make it a habit of schema-qualifying table names to avoid ambiguity.

##FETCH_STATUS lives outside the cursor

I have a query which processes XML data and I use a while(##FETCH_STATUS = 0) loop for data returned from the cursor.
When I run the query using Management Studio, ##FETCH_STATUS equals -1 and the code inside my loop is omitted. If I run the query using the debugger and press continue, it runs just fine and the ##FETCH_STATUS equals 0. When I run the query again, after running it in debug ##FETCH_STATUS equals 0 and changes to -1.
To sum up:
I run with SSMS - ##FETCH_STATUS = -1
I run with debugger - ##FETCH_STATUS = 0 (I want this value)
I run with SSMS once after running with debugger ##FETCH_STATUS still equals 0 but then changes to -1.
I use OPEN cursor, CLOSE cursor and DEALLOCATE cursor. Why does it work this way?
EDIT: Code you asked for:
IF (OBJECT_ID('dbo.XmlOrderResponses') IS NOT NULL)
DROP TABLE XmlOrderResponses;
CREATE TABLE XmlOrderResponses (
OrderResponseType INT
,OrderResponseNumber NVARCHAR(40)
,OrderResponseDate DATETIME
,DocumentFunctionCode NVARCHAR(40)
,Remarks INT
);
DECLARE CUR CURSOR
FOR
SELECT Subdirectory
FROM XMLFiles;
OPEN CUR
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #DocHandle AS INT;
DECLARE #TMP AS NVARCHAR(512);
FETCH NEXT
FROM Cur
INTO #TMP
DECLARE #XmlDocument AS NVARCHAR(MAX);
SET #XmlDocument = (
SELECT CAST(XMLSource AS NVARCHAR(max))
FROM XMLFiles
WHERE subdirectory = #TMP
);
EXEC sys.sp_xml_preparedocument #DocHandle OUTPUT
,#XmlDocument;
INSERT INTO XmlOrderResponses (
OrderResponseType
,OrderResponseNumber
,OrderResponseDate
,DocumentFunctionCode
,Remarks
)
SELECT *
FROM OPENXML(#DocHandle, '/Document-OrderResponse/*', 11) WITH (
OrderResponseType INT
,OrderResponseNumber NVARCHAR(40)
,OrderResponseDate DATETIME
,DocumentFunctionCode NVARCHAR(40)
,Remarks INT
);
EXEC sys.sp_xml_removedocument #DocHandle;
END
CLOSE CUR;
DEALLOCATE CUR;
--I know I shouldn't be doing that but I can't get rid of NULL records the other way.
DELETE
FROM XmlOrderResponses
WHERE OrderResponseType IS NULL
AND OrderResponseNumber IS NULL
AND OrderResponseDate IS NULL
AND DocumentFunctionCode IS NULL
AND Remarks IS NULL;
SELECT *
FROM XmlOrderResponses
SELECT ##FETCH_STATUS
The problem is that the first time you refer to ##FETCH_STATUS, you have not done a fetch with your cursor, so it is referring to the last cursor used. Imagine this simple example:
DECLARE C1 CURSOR
FOR
SELECT TOP 3 ID
FROM (VALUES ('1'), ('2'), ('3')) t (ID);
OPEN C1;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #c1 CHAR(1);
FETCH NEXT FROM C1 INTO #c1;
PRINT #c1;
END
CLOSE C1;
DEALLOCATE C1;
DECLARE C2 CURSOR
FOR
SELECT TOP 3 ID
FROM (VALUES ('1'), ('2'), ('3')) t (ID);
OPEN C2;
-- HERE ##FETCH_STATUS REFERS TO THE LAST FETCH FOR CURSOR `C1` NOT `C2`
SELECT ##FETCH_STATUS;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #c2 CHAR(1);
FETCH NEXT FROM C2 INTO #c2;
PRINT #c2;
END;
CLOSE C2;
DEALLOCATE C2;
At the commented line, even though you have closed, and deallocated C1, ##FETCH_STATUS is still referring to this cursor (since no other FETCH has been performed since), so you never enter your loop for C2
You should perform the Fetch before the loop, then at the end of each loop, rather than at the beginning.
DECLARE #TMP AS NVARCHAR(512);
OPEN CUR
-- DO FETCH FIRST
FETCH NEXT FROM Cur INTO #TMP
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #DocHandle AS INT;
-- DO ALL YOUR WORK WITH #TMP
--PERFORM THE FETCH AGAIN AT THE END OF THE LOOP
FETCH NEXT FROM Cur INTO #TMP
END
The other problem you have with doing FETCH at the start of each loop, is that the last item will be processed twice. Again a simple example (and assuming you enter the loop with ##FETCH_STATUS = 0)
DECLARE C1 CURSOR
FOR
SELECT ID = '1';
OPEN C1;
DECLARE #c CHAR(1);
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #c1 CHAR(1);
FETCH NEXT FROM C1 INTO #c1;
PRINT #c1;
END
This will print
1
1
Because, when ##FETCH_STATUS is -1, FETCH will just return the item at the current position.

Ifexists() tsql looping issue

DECLARE #tag VARCHAR(MAX)
DECLARE #TagID as INT;
DECLARE #ID as INT;
DECLARE tag_cursor CURSOR
FOR
SELECT tagname FROM #temptag
FOR READ ONLY
OPEN tag_cursor
FETCH NEXT FROM tag_cursor INTO #tag
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS (SELECT TOP 1 * FROM Tag WHERE TagName=#tag)
BEGIN
print 1;
END
print 2;
/* INSERT INTO Tag
SELECT #tag FROM #temptag
SELECT #TagID = SCOPE_IDENTITY();
print #TagID*/
FETCH NEXT FROM tag_cursor INTO #tag
END
CLOSE tag_cursor
DEALLOCATE tag_cursor
In my stored procedure I am searching for a value in the table. If the value already exists then I should not insert value.
so I have written
IF EXISTS (SELECT TOP 1 * FROM Tag WHERE TagName=#tag)
BEGIN
print 1;
END
print 2;
/* INSERT INTO Tag
SELECT #tag FROM #temptag
SELECT #TagID = SCOPE_IDENTITY();
print #TagID*/
FETCH NEXT FROM tag_cursor INTO #tag
END
Problem: When I run it both values print1 and print 2 are getting printed
Can someone help me in fixing the error
Perhaps you meant to have an ELSE block?
IF EXISTS (SELECT TOP 1 * FROM Tag WHERE TagName=#tag)
BEGIN
print 1;
END
ELSE
BEGIN
print 2;
/* INSERT INTO Tag
SELECT #tag FROM #temptag
SELECT #TagID = SCOPE_IDENTITY();
print #TagID*/
END

Resources