Cursor - Declare Select-Statement executed multiple times? - sql

I've got a question regarding cursor in t-sql.
when i do a cursor like that, it will end up in an endless loop.
drop table [dbo].[cursorcheck]
GO
CREATE TABLE [dbo].[cursorcheck](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Wert] [varchar](10) NOT NULL
) ON [PRIMARY]
GO
delete dbo.cursorcheck
GO
insert into dbo.cursorcheck
select 'Insert'
GO
DECLARE #vendor_id int, #vendor_name nvarchar(50);
DECLARE vendor_cursor CURSOR FOR
SELECT ID, wert
FROM dbo.cursorcheck
WHERE wert = 'insert';
OPEN vendor_cursor
FETCH NEXT FROM vendor_cursor
INTO #vendor_id, #vendor_name
WHILE ##FETCH_STATUS = 0
BEGIN
insert into dbo.cursorcheck
select 'Insert'
FETCH NEXT FROM vendor_cursor
INTO #vendor_id, #vendor_name
END
CLOSE vendor_cursor;
DEALLOCATE vendor_cursor;
I'm a bit confused about this behavior. Does that mean that the select script, the cursor was declared for, is executed multiple times? i got the same effect when i insert into that table from another transaction while debugging the cursor. the problem is solved when i use the "static" keyword for declaring the cursor. but i'm confused about the behavior of the cursor when i don't use the static keyword.
any explanation?
Regards,
Reto

The key is you try to read and insert the same table in loop (so it is like "a snake eating own tail"):
Cursor:
DECLARE vendor_cursor CURSOR FOR
SELECT ID, wert
FROM dbo.cursorcheck
WHERE wert = 'insert';
And loop:
WHILE ##FETCH_STATUS = 0
BEGIN
insert into dbo.cursorcheck
select 'Insert'
FETCH NEXT FROM vendor_cursor
INTO #vendor_id, #vendor_name
END
For second question: The problem is solved when i use the "static":
STATIC CURSOR
The complete result set of a static cursor is built in tempdb when the
cursor is opened. A static cursor always displays the result set as it
was when the cursor was opened.
and:
The cursor does not reflect any changes made in the database that
affect either the membership of the result set or changes to the
values in the columns of the rows that make up the result set. A
static cursor does not display new rows inserted in the database after
the cursor was opened, even if they match the search conditions of the
cursor SELECT statement

Related

how to print my cursor data values instead to insert in sql server?

I have following sql server cursor code to insert data.
Create table #tmp
(
Ref_Code UNIQUEIDENTIFIER,
Create_UserId UNIQUEIDENTIFIER
)
--simple cursor in sql server
Declare #Ref_Code UNIQUEIDENTIFIER , #Create_UserId UNIQUEIDENTIFIER
-- declare a cursor
DECLARE insert_cursor CURSOR FOR
SELECT Ref_Code,Create_UserId from Account_APInvoiceDetail
WHERE Ref_Code = '355199F6-0DE1-44AF-A8B2-2B1592980688';
-- open cursor and fetch first row into variables
OPEN insert_cursor
FETCH NEXT FROM insert_cursor into #Ref_Code,#Create_UserId
-- check for a new row
WHILE ##FETCH_STATUS=0
BEGIN
-- do complex operation here
Insert into #tmp
SELECT #Ref_Code,#Create_UserId
-- get next available row into variables
FETCH NEXT FROM insert_cursor into #Ref_Code,#Create_UserId
END
close insert_cursor
Deallocate insert_cursor
GO
but now i need print this data in my console as well. how can i do this?
Thanks.

TSQL not causing infinite loop

Please see the code below:
declare #crimeurn varchar(20)
DECLARE #finalresults TABLE (crime_urn varchar(20))
DECLARE #potentialresults TABLE (crime_urn varchar(20))
insert into #finalresults values ('1')
DECLARE finalresults_cursor CURSOR FOR
SELECT crime_urn FROM #finalresults
OPEN finalresults_cursor
FETCH NEXT FROM finalresults_cursor INTO #crimeurn
WHILE ##FETCH_STATUS = 0
BEGIN
print #crimeurn
INSERT INTO #finalresults
values ('2')
FETCH NEXT FROM finalresults_cursor INTO #crimeurn
END
select * from #finalresults --line 16
CLOSE finalresults_cursor
DEALLOCATE finalresults_cursor
Line 16 displays 5137 or 12,342 rows in SQL studio manager (it randomly varies). I expected the TSQL to cause an infinite loop because there is an insert into the table variable on every iteration of the cursor.
Why does it not cause an infinite loop? i.e. why are there 5,137 or 12,342 rows returned.
You are inserting into a heap.
A heap is unordered. There is no particular guarantee that the row will be inserted after the current row and picked up on the next fetch.
I made a slight amend to your test framework and added an IDENTITY column. In my case it got to row 592,353 before terminating.
As you can see from the results below this final row happened to be inserted on an earlier page in the file (jumped from 1623 to 184) so an allocation ordered scan starting from the penultimate row wouldn't find it.
Code to reproduce.
declare #crimeurn varchar(20)
DECLARE #finalresults TABLE (crime_urn varchar(20), ID int identity)
DECLARE #potentialresults TABLE (crime_urn varchar(20))
insert into #finalresults values ('1')
DECLARE finalresults_cursor CURSOR FOR
SELECT crime_urn FROM #finalresults
OPEN finalresults_cursor
FETCH NEXT FROM finalresults_cursor INTO #crimeurn
WHILE ##FETCH_STATUS = 0
BEGIN
print #crimeurn
INSERT INTO #finalresults
--OUTPUT INSERTED.ID
values ('2')
FETCH NEXT FROM finalresults_cursor INTO #crimeurn
END
select *, sys.fn_PhysLocFormatter(%%physloc%%) from #finalresults --line 16
ORDER BY ID
CLOSE finalresults_cursor
DEALLOCATE finalresults_cursor
Edit: The information below is wrong, but I've left it because that's how I believe it's supposed to work.
By default, cursors do not run in INSENSITIVE or STATIC mode. By default cursors are DYNAMIC and OPTIMISTIC. The documentation on cursors doesn't mention how dynamic cursors behave with respect to INSERTS. INSERT behavior appears to be undocumented.
You may be able to fix this with the SCROLL_LOCKS option, which guarantees order preservation.
Because the cursor's definition is fixed when you run
DECLARE finalresults_cursor CURSOR FOR
SELECT crime_urn FROM #finalresults
It's static after that point. Updating the table variable #finalresults doesn't change the cursor finalresults_cursor.
It's like this:
X = 10
Y = X
X = 20
PRINT X, Y
Outputs this:
20 10
However, if you do not care or know the type of the cursor, you can use the ##CURSOR_ROWS inside your loop to do some "cursor" logic :) .
Here is some documentation on the possible values the ##CURSOR_ROWS variable can have, depending on the cursor's type: .

If statement in AFTER DELETE trigger not working

I have a trigger which I would like to do something if a certain condition is met.
If a user causes the system to remove a row from a security table, and that row happens to fit particular criteria, I want to run a stored proc. The problem that I'm running into is that the trigger behaves as though the if statement's criteria is not met. I know that the criteria is being met, because I tried piping the variable in question into a table, and the value I came up with was correct.
If I remove the IF statement, the procedure runs (though indiscriminately...it runs as expected, regardless of the value of the personorgroup field in the deleted table, which is not what I want it to do.)
Here is the trigger:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
alter TRIGGER dbo.SecurityPersonRemoval
ON dbo.[security]
AFTER delete
AS
BEGIN
SET NOCOUNT ON;
declare #person int
declare #thing int
select #person = personorgroup from deleted
select #thing = thing from deleted
if #person = '37671721'
exec docsadm.ethicalwall #thing
END
GO
Ideas?
Your statement probably fails because "deleted" can contain multiple records. Assigning the value of the select to a scalar will only return the first personID and the rest will be ignored. If you must call docsamd.ehticalwall for each person deleted, you could use a cursor:
declare #personId int,
#thingId int,
#fetchStatus int
declare myDeletedPeople cursor for select personorgroup, thing from deleted readonly
open myDeletedPeople
fetch myDeletedPeople into #personId, #thingId
select #fetchStatus = ##FETCH_STATUS
while (#fetchStatus=0)
begin
if(#personId = '37671721')
begin
exec docsadm.ethicalwall #thingId
end
fetch myDeletedPeople into #personId, #thingId
select #fetchStatus = ##FETCH_STATUS
end
close myDeletedPeople
deallocate myDeletedPeople

How to get the first row details using rowcount?

I have a temp variable called #rows having nearly 10000 records in a stored procedure like this
Create Procedure input
as
begin
declare #input_data table(......)
insert into (.....) from ....
#rows= select ##rowcount
while(#rows > o)
begin
--- I need to process each row like
select ... where #row=1 --like this repeatedly upto #rows = 10000
end
How should I achieve this.Please help me
Thanks in advance
You can directly update the table based on some conditiuons and using CASE statement insted of using while loop.
You may achieve your goal using a CURSOR
DECLARE #ID AS INT
DECLARE TestCursor CURSOR
FOR (SELECT ID FROM Test)
OPEN TestCursor
FETCH NEXT FROM TestCursor INTO #ID
WHILE ##Fetch_Status = 0
BEGIN
--Your Code Here
PRINT #ID --Print For Testing
FETCH NEXT FROM TestCursor INTO #ID
END
CLOSE TestCursor
DEALLOCATE TestCursor
Remember : using cursors will lead to a performance loss
Instead use CASE statements in queries for conditional selections/updates, as described in another answer

How to replace cursor code with CTE in sql server 2008

I have a condition where I need to pull id's from one master table and then based on that value pull values from two different tables and then update/insert into third table with these values.
I am using cursor to loop through the records in master table however I feel like this is leading to performance issue. I wanted to know if this can be done using CTE or not. I tried using CTE but it is non recursive and I am totally lost where I am going wrong.
Here is how my stored procedure is -
//
BEGIN
declare #variables char(10);
DECLARE #cursor CURSOR; --DECLARE CURSOR
SET #cursor = CURSOR FOR -- SET CURSOR
-- pull in value for curso
OPEN #cursor
FETCH NEXT
FROM #cursor INTO #variables --FILL IN CURSOR TO LOOP THROUGH
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN
--PUll values from table 1
END
BEGIN
--Pull values from table 1
-- Do some maths on the values pulled
END
BEGIN
--function/sql to update or insert
END
FETCH NEXT
FROM #cursor INTO #variables;
END
CLOSE #cursor;
DEALLOCATE #cursor;
END
//
With CTE, my code is -
//
;WITH CTE AS
(
--pull values from master table
)
BEGIN
BEGIN
-- pull values from table 1
END
BEGIN
-- Pull values from table 2, do the calculations
END
BEGIN
-- insert or update as needed.
END
//