Infinite loop in T-SQL cursor - tsql

Below is the code for a T-SQL cursor. It works fine through the first iteration, however after that gets stuck in an infinite loop going between the FETCH NEXT statement and the IF NOT EXISTS statement (basically it will insert the first record, however after that the cursor will not move onto the next record so the IF NOT EXISTS is perpetually false). This is my first time using a cursor so was hoping someone could explain what is going on/how to make this thing work!
DECLARE prod_cursor CURSOR FOR
SELECT ProductCode
FROM CourseToProduct
WHERE CourseCode = #courseCode and (TerminationDate >= #expDate OR TerminationDate IS NULL)
OPEN prod_cursor
FETCH NEXT FROM prod_cursor
INTO #productCode
WHILE ##FETCH_STATUS = 0
BEGIN
IF NOT EXISTS
(
SELECT sNumber
FROM AgentProductTraining
WHERE #sNumber = sNumber and
#courseCode = CourseCode and
#productCode = ProductCode and
#dateTaken = DateTaken
)
BEGIN
IF #sNumber IS NOT NULL
BEGIN
INSERT INTO AgentProductTraining
(
sNumber,
CourseCode,
ProductCode,
DateTaken,
DateExpired,
LastChangeOperator,
LastChangeDate
)
VALUES (
#sNumber,
#courseCode,
#productCode,
#dateTaken,
COALESCE(#expDate, 'NULL'),
#lastChangeOperator,
#lastChangeDate
)
END
END
END
CLOSE prod_cursor;
DEALLOCATE prod_cursor;

You have to fetch the next row in the while..end or it will never move to the next record. Like this:
DECLARE prod_cursor CURSOR FOR
SELECT ProductCode
FROM CourseToProduct
WHERE CourseCode = #courseCode and (TerminationDate >= #expDate OR TerminationDate IS NULL)
OPEN prod_cursor
FETCH NEXT FROM prod_cursor
INTO #productCode
WHILE ##FETCH_STATUS = 0
BEGIN
IF NOT EXISTS
(
SELECT SymetraNumber
FROM AgentProductTraining
WHERE #symetraNumber = SymetraNumber and
#courseCode = CourseCode and
#productCode = ProductCode and
#dateTaken = DateTaken
)
BEGIN
IF #symetraNumber IS NOT NULL
BEGIN
INSERT INTO AgentProductTraining
(
sNumber,
CourseCode,
ProductCode,
DateTaken,
DateExpired,
LastChangeOperator,
LastChangeDate
)
VALUES (
#sNumber,
#courseCode,
#productCode,
#dateTaken,
COALESCE(#expDate, 'NULL'),
#lastChangeOperator,
#lastChangeDate
)
END
END
FETCH NEXT FROM prod_cursor
INTO #productCode
END
CLOSE prod_cursor;
DEALLOCATE prod_cursor;

Related

in if else Loop ,cannot drop the same temp table

i am using if else statement and ,i want to drop the temporary table which is created out side the inner if statement , but when i execute the statement , i get the answer as , There is already an object named '#table' in the database. ---------------------MY CODE IS LIKE enter code here
DECLARE #RowCount INT;
SET #RowCount = 1;
IF #RowCount = 1
BEGIN
IF OBJECT_ID('tempdb..#Guarantor_Details') IS NOT NULL
DROP TABLE #Guarantor_Details
SELECT DISTINCT TOP 1 GUARANTORS.GUARANTOR_CODE
INTO #Guarantor_Details
FROM GUARANTORS
WHERE ISNULL(GUARANTORS.deleted, 0) <> 1;
DECLARE #GuarantorCount INT;
SELECT #GuarantorCount = count(*)
FROM #Guarantor_Details;
IF (#GuarantorCount = 0)
DROP TABLE #Guarantor_Details
BEGIN
IF OBJECT_ID('tempdb..#Guarantor_Details') IS NOT NULL
SELECT TOP 1 CLIENT.Last_Name
INTO #Guarantor_Details
FROM CLIENT
LEFT JOIN GUARANTORS ON GUARANTORS.GUARANTOR_CODE = CLIENT.Financial_Guarantor
END
END
------------------------------------------------------------------------
You don't need to do this type of CREATE / DROP. You can (and probably should) create the table before the loop and simply either TRUNCATE TABLE or DELETE FROM the temp table when necessary to "reset" it. Just pick a datatype that fits both GUARANTOR_CODE and Last_Name.
You also don't need to do a SELECT COUNT(*) on the table when you have the ##ROWCOUNT from the INSERT operation that already has that value in it.
Essentially:
CREATE TABLE #Guarantor_Details (Value NVARCHAR(50));
WHILE (something)
BEGIN
IF (#RowCount = 1)
BEGIN
TRUNCATE TABLE #Guarantor_Details;
INSERT INTO #Guarantor_Details (Value)
SELECT DISTINCT TOP 1 GUARANTORS.GUARANTOR_CODE
FROM GUARANTORS
WHERE ISNULL(GUARANTORS.deleted, 0) <> 1;
IF (##ROWCOUNT = 0)
BEGIN
INSERT INTO #Guarantor_Details (Value)
SELECT TOP 1 CLIENT.Last_Name
FROM CLIENT
LEFT JOIN GUARANTORS
ON GUARANTORS.GUARANTOR_CODE = CLIENT.Financial_Guarantor;
END;
END;
FYI, there appear to be some logic problems in the original code:
There are BEGIN / END tags at the bottom of the IF #RowCount = 1 block that aren't associated to any IF or WHILE. This is technically ok, but usually implies something is missing.
If this code is all of the code, then the IF #RowCount = 1 doesn't work in the loop unless something sets #RowCount past the end of this outer IF block that is not shown here in the question.

rollback a nested transaction in trigger

I have a following situation.
I have a table with trigger for insert.
When I insert a row in it, from this trigger I want to insert some rows into a second table.
For each of these rows I want to do it in it's own transaction in case something go wrong.
I want to have original row in first table and all rows (these withous errors) in the second.
A little code to reproduce:
create table test(id int primary key identity(1,1),name nvarchar(10))
create table test2(id int primary key identity(1,1),
state char(1) check (state in ('a','b')))
go
create trigger test_trigger on test for insert
as
begin
declare #char char(1)
declare curs cursor for (
select 'a'
union
select 'c'
union
select 'b')
open curs
fetch next from curs into #char
while ##FETCH_STATUS = 0
begin
begin try
begin transaction
insert into test2(state)
select #char
commit
end try
begin catch
print 'catch block'
rollback
end catch
fetch next from curs into #char
end
close curs
deallocate curs
end
go
insert into test(name) values('test')
select * from test
select * from test2
go
drop table test
drop table test2
So for the sample data from this snippet, I want to have a row with 'test' in test table and two rows in the test2 table ('a' and 'b').
How can I write a code for that?
Looks like finally I got it to work.
Corrected trigger code:
create trigger test_trigger on test for insert
as
begin
declare #char char(1)
set xact_abort off
declare curs cursor for (
select 'a'
union
select 'c'
union
select 'b')
open curs
fetch next from curs into #char
while ##FETCH_STATUS = 0
begin
save transaction t
begin try
insert into test2(state)
select #char
end try
begin catch
print 'catch block'
rollback transaction t
end catch
fetch next from curs into #char
end
close curs
deallocate curs
end

Sql Server Any data from the row itself with Try Catch?

Using Try/Catch with SqlServer 2008R2, is there a trick to getting some information out of the row that caused the error? For example,
BEGIN TRY
INSERT INTO MyTable
SELECT *
FROM #MyTableVar
END TRY
BEGIN CATCH
-- In here, is there some way to know, for example, MyTable.SomeColumn for the offending row?
END CATCH
This is what I ended up doing:
DECLARE #MyResults TABLE (
Id INT IDENTITY( 1, 1 )
TheKey VARCHAR(20),
Success BIT
)
-- Initially set Success to 1 for all rows
INSERT INTO #MyResults
SELECT TheKey, 1
FROM #MyTableVar
DECLARE #CurrentKey VARCHAR(20)
DECLARE #CurrentId INT
DECLARE incoming CURSOR FOR SELECT Id, TheKey FROM #MyResults
OPEN incoming
FETCH incoming into #Id, #CurrentKey
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
INSERT INTO OfficialTable
SELECT *
FROM #MyTableVar TV
WHERE TV.TheKey = #CurrentKey
END TRY
BEGIN CATCH
-- On any failure, update the proper row in #MyResults
UPDATE #MyResults
SET Success = 0
WHERE TheKey = #CurrentKey AND Id = #CurrentId
END CATCH
FETCH NEXT FROM incoming INTO #Id, #CurrentKey
END
CLOSE incoming
SELECT * FROM #MyResults
In the CATCH, I know the key to #MyTableVar, so, I should be able to look up anything I need with that.

TSQL Cursor Infinite Loop without TOP xxx in SELECT Statement

I have a curious problem with an infinite loop in a TSQL cursor. The cursor loops infinitely when I do not add TOP 300 to the defining select statement of the cursor. The following is an example of the code: Any assistance to this issue is much appreciated.
DECLARE #Done BIT
SET #Done = 0
DECLARE cursOut CURSOR LOCAL FAST_FORWARD
FOR
SELECT
--TOP 300
FirstName FirstName
,LastName LastName
,MiddleName MiddleName
,Email Email
,Address1 Address1
,Address2 Address2
,City City
,[State] [State]
FROM StagedUsers
OPEN cursOut;
WHILE (#Done = 0)
BEGIN
--Fetch next row
FETCH NEXT
FROM cursOut
INTO ,#v_FirstName
,#v_LastName
,#v_MiddleName
,#v_Email
,#v_Address1
,#v_Address2
,#v_City
,#v_State
IF (##FETCH_STATUS <> 0)
BEGIN
SET #Done = 1
BREAK
END
--if #batch = 0
BEGIN TRANSACTION
--process statements
--updates or insert statements
--Commit transaction
COMMIT TRANSACTION
--End While
END
--CleanUp:
CLOSE cursOut
DEALLOCATE cursOut
Thanks,
Renegrin
First I think you do not need transaction here, I presume it is only one statement executed, so remove transaction from code.
Second do it without #done flag, it is confusing (it is probably not problem here).
DECLARE #v_FirstName VARCHAR(500),#v_LastName VARCHAR(500),#v_MiddleName VARCHAR(500),#v_Email VARCHAR(500),#v_Address1 VARCHAR(500),#v_Address2 VARCHAR(500),#v_City VARCHAR(500),#v_State VARCHAR(500)
DECLARE cursOut CURSOR FAST_FORWARD FOR
SELECT FirstName FirstName,LastName LastName,MiddleName MiddleName,Email Email,Address1 Address1,Address2 Address2,City City,[State] [State]
FROM StagedUsers
OPEN cursOut;
FETCH NEXT FROM cursOut INTO #v_FirstName,#v_LastName,#v_MiddleName,#v_Email,#v_Address1,#v_Address2,#v_City,#v_State
declare #i int = 1
WHILE ##FETCH_STATUS = 0
BEGIN
print cast(#i as varchar(10))
--> statement here
FETCH NEXT FROM cursOut INTO #v_FirstName,#v_LastName,#v_MiddleName,#v_Email,#v_Address1,#v_Address2,#v_City,#v_State
set #i = #i + 1
END
CLOSE cursOut
DEALLOCATE cursOut
Do you want to update current row where cursor point? If so, there is way to do it.
Is there any indexes on table? Can you post an update query?

How to use WITH table AS result within cursor loop to run stored procedure

How to get result in WITH table AS into CURSOR loop? I have previously asked about how to get recursive results from my table
How to read all records recursively and show by level depth TSQL
;with C as
(
definition ...
)
I have created CURSOR loop where I want to run specific stored procedure for all results in table
declare #id int, #parent int
declare cur cursor local fast_forward
for
select id, parent from C
open cur
fetch next from cur into #id, #parent
while ##fetch_status = 0
begin
exec storedProcedure #id=#id, #parent=#parent
fetch next from cur into #id, #parent
end
close cur
deallocate cur
Problem is that CURSOR doesnt know table from WITH AS result.
Invalid object name 'C'.
You can create a temp table or a table variable to hold the rows returned by you CTE query and then you use that table as the source for your cursor.
declare #T table
(
id int,
parent int
)
;with C as
(
select 1 as id, 2 as parent
)
insert into #T
select id, parent
from C
declare cur cursor for select id, parent from #T