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
Related
How can we remove cursor in the following scenario:
DECLARE #Tablesq TABLE (numbercol INT)
INSERT INTO #Tablesq
SELECT 25 UNION all
SELECT -25 UNION all
SELECT 25 UNION all
SELECT 36
DECLARE #number INT
DECLARE sqrtcur CURSOR FOR SELECT numbercol FROM #tablesq
OPEN sqrtcur
FETCH NEXT FROM sqrtcur INTO #number
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
SELECT SQRT(#number)
END TRY
BEGIN CATCH
print ERROR_MESSAGE();
END CATCH
FETCH NEXT FROM sqrtcur INTO #number
END
CLOSE sqrtcur
DEALLOCATE sqrtcur
Here -25 value is causing the error and because of which I am forced to use the cursor. Can we do a set-based operation to achieve the same result?
SQL Server does not provide very good error handling within a SELECT statement. The one exception is for type conversion and the try_ functions -- but even that is relatively recently in the history of the database.
SQL is not alone in this. The standard just does not address this point.
The one thing you can do is use a case expression, if you understand the conditions when you will get an error:
select (case when numbercol >= 0 then sqrt(numbercol) end)
from tablesq;
SQL Server guarantees that the when conditions are evaluated in order, stopping at the first match, and the then is evaluated after then when. I should say "under almost all circumstances"; there are some optimizations made in aggregation queries that somewhat weaken that statement.
Many other databases have similar limitations. And in most of them, you can use a user-defined function that catches the exception. Alas, SQL Server does not allow try/catch blocks in UDFs, so this is not an option.
Are you looking for this:
SELECT SQRT(numbercol)
FROM #Tablesq
WHERE numbercol > 0;
You can do it this way :
DECLARE #tablesq TABLE (numbercol INT)
INSERT INTO #tablesq
SELECT 25 UNION all
SELECT -25 UNION all
SELECT 25 UNION all
SELECT 36;
DECLARE #number INT;
DECLARE sqrtcur CURSOR FOR SELECT numbercol FROM #tablesq;
DECLARE #TEST FLOAT;
OPEN sqrtcur;
FETCH NEXT FROM sqrtcur INTO #number;
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
SET #TEST = SQRT(#number);
SELECT SQRT(#number);
END TRY
BEGIN CATCH
print ERROR_MESSAGE();
END CATCH;
FETCH NEXT FROM sqrtcur INTO #number;
END;
CLOSE sqrtcur;
DEALLOCATE sqrtcur;
SQL-Server Code:
Declare #offers VARCHAR(100)
Declare #offers_seq VARCHAR(100)
Declare Result Cursor
For Select Top 1 Offers,Offers_seq From [REZJQWB01]..ActiveBooking_OffersDetails_Seq
Open Result
While ##fetch_status=0
Begin
Fetch Next From Result Into #offers, #offers_seq
Declare #value VARCHAR(100) = #offers
While len(#value) >= 1
Begin
Set #value = substring(#value,charindex(';',#value)+1,len(#value))
Print #value
End
End
Close Result
Deallocate Result
What I'm trying to accomplish here is to split a set of delimited values present in one cell and then creating a cursor for the complete column. The first time I run this code it gives the below output:
2;6;7;8;9;12;13;14;17;19;21;
6;7;8;9;12;13;14;17;19;21;
7;8;9;12;13;14;17;19;21;
8;9;12;13;14;17;19;21;
9;12;13;14;17;19;21;
12;13;14;17;19;21;
13;14;17;19;21;
14;17;19;21;
17;19;21;
19;21;
21;
2;6;7;8;9;12;13;14;17;19;21;
6;7;8;9;12;13;14;17;19;21;
7;8;9;12;13;14;17;19;21;
8;9;12;13;14;17;19;21;
9;12;13;14;17;19;21;
12;13;14;17;19;21;
13;14;17;19;21;
14;17;19;21;
17;19;21;
19;21;
21;
Ideally It should Print only once but I'm not sure why the loop runs twice. The second time I run this, it gives the output as : 'Command(s) completed successfully'.
Kindly help.
Thanks.
The reason it is running twice is that you aren't doing the fetch until after you've already checked the ##fetch_status.
The steps look like this:
Check ##fetch_status, which is zero, since nothing has been fetched.
Fetch a result
Run your substring code
Check ##fetch_status again, which is still zero, because a record was fetched in the previous step
Fetch another result, which fails but the cursor is still pointing at the same row as before
Run your substring code again, same result
Check ##fetch_status again, which now returns -1 because the previous fetch failed.
For the same reason as you get two results from running it once, you get nothing the second time, because ##fetch_status is -1 from the previous execution. To fix both issues, you need to fetch before checking the status. Usually you'll see one of the following methods employed (psuedocode left as an exercise for you to implement). Typically I use the first option, but some find the second is easier to read:
-- (declare and open cursor)
while 1=1 begin
fetch next from cursor
if ##fetch_status <> 0 break;
-- (do stuff)
end
or
-- (declare and open cursor)
fetch next from cursor
while ##fetch_status = 0 begin
-- (do stuff)
fetch next from cursor
end
Correct Code:
Declare #offers VARCHAR(100)
Declare #offers_seq VARCHAR(100)
Declare Result Cursor
For Select Top 1 Offers,Offers_seq From [REZJQWB01]..ActiveBooking_OffersDetails_Seq
Open Result
While 1=1
Begin
Fetch Next From Result Into #offers, #offers_seq
If ##fetch_status <> 0 Break;
Declare #value VARCHAR(100) = #offers
While len(#value) > 1
Begin
Set #value = substring(#value,charindex(';',#value,2)+1,len(#value))
Set #value = ';'+#value
If len(#value) <= 1 Break;
Print #value
End
End
Close Result
Deallocate Result
Result:
;6;7;8;9;12;13;14;17;19;21;
;7;8;9;12;13;14;17;19;21;
;8;9;12;13;14;17;19;21;
;9;12;13;14;17;19;21;
;12;13;14;17;19;21;
;13;14;17;19;21;
;14;17;19;21;
;17;19;21;
;19;21;
;21;
In #proc_name table variable, I have stored procedure name and I am passing parameters using dynamic SQL. I am using a while loop to loop through all rows in #proc_name. Can I use a CTE here to improve performance?
SELECT *
FROM #proc_name
WHILE (#count <= #max)
BEGIN
SET #proc_exec = 'usp_Balance_'
+Replace((SELECT Description FROM #proc_name WHERE rn= #count),' ','')+' '+' '+''''
+Replace((SELECT TellerID FROM #proc_name WHERE rn= #count),' ','')+''''+' ,'+''''
+#LocationID+''''+' , '+''''+cONvert(VARCHAR(100),#BusinessDate)+''''
-- Update TDrawerSummary
PRINT (#proc_exec)
SET #count = #count + 1
END
Thank you.
To start with, replace the WHILE loop by a cursor as shown below:
DECLARE c CURSOR FOR
SELECT 'usp_Balance_'
+Replace(Description,' ','')+' '+' '+''''
+Replace(TellerID,' ','')+''''+' ,'+''''
+#LocationID+''''+' , '+''''+cONvert(VARCHAR(100),#BusinessDate)+''''
FROM #proc_name
ORDER BY rn
OPEN c
FETCH c INTO #proc_exec
WHILE ##FETCH_STATUS=0
BEGIN
-- Update TDrawerSummary
PRINT #proc_exec
FETCH c INTO #proc_exec
END
CLOSE c
DEALLOCATE c
In some cases you could use a CTE to help convert procedural code into a query. This could improve performance. If you can share the code that is building #proc_name I could check if there is a possibility for this.
Inside the cursor you should consider to take some measures that will help you track down errors. In the current code, it will be quite hard to know in which iteration an error happened. A simplified example is shown below:
BEGIN TRY
-- Update TDrawerSummary
PRINT #proc_exec
END TRY
BEGIN CATCH
PRINT 'Failed executing '+COALESCE(#proc_exec,'(null)')+': '+ERROR_MESSAGE()
END CATCH
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
I want to reference the nth row of the #temptable (at the second SQL comment is below). What expression will allow me to do so?
DECLARE #counter INT
SET #counter = 0
WHILE (#counter<count(#temptable))
--#temptable has one column and 0 or more rows
BEGIN
DECLARE #variab INT
EXEC #variab = get_next_ticket 3906, 'n', 1
INSERT INTO Student_Course_List
SELECT #student_id,
-- nth result set row in #temptable, where n is #count+1
#variab
SET #counter = #counter +1
END
Cursor (will this work?):
for record in (select id from #temptable) loop
--For statements, use record.id
end loop;
Normally in a relational database like SQL Server, you prefer to do set operations. So it would be best to simply have INSERT INTO tbl SOMECOMPLEXQUERY even with very complex queries. This is far preferable to row processing. In a complex system, using a cursor should be relatively rare.
In your case, it would appear that the get_next_ticket procedure performs some significant logic which is not able to be done in a set-oriented fashion. If you cannot perform it's function in an alternative set-oriented way, then you would use a CURSOR.
You would declare a CURSOR on your set SELECT whatever FROM #temptable, OPEN it, FETCH from the cursor into variables for each column and then use them in the insert.
Instead of using a while loop (with a counter like you are doing) to iterate the table you should use a cursor
Syntax would be:
DECLARE #id int
DECLARE c cursor for select id from #temptable
begin
open c
fetch next from c into #id
WHILE (##FETCH_STATUS = 0)
BEGIN
--Do stuff here
fetch next from c into #id
END
close c
deallocate c
end