DB Cursor FETCH NEXT woks but FETCH FIRST does not - sql

Because of some complex work, I'm resorting to using a DB cursor. I also have to traverse through the qualifying records in a table from beginning to end more than one time. When I reach last record in the table, I'd reset the cursor to the beginning of the table again for the next pass through.
However, FETCH NEXT works fine, if I try FETCH FIRST, I get an error: ##FETCH_STATUS = -1. What's wrong with my cursor?
FETCH FIRST db_cursor INTO #VendorId, #VendorCount
verses
FETCH NEXT FROM db_cursor INTO #VendorId, #VendorCount
Using SQL Server 2017.

SCROLL works great
DECLARE db_cursor CURSOR
SCROLL
FOR
SELECT VendorId, VendorCount
FROM #venderData t
WHERE PageIndex IS NULL
ORDER BY VendorCount DESC;

Related

loop through results and create views for a new database

I am using SQL Server 2012.
I am copying some view from one database to another one. I know I can use Task > Generate scripts to do this for me, however I would like to know how to do this in a different way.
If I run the query select * FROM INFORMATION_SCHEMA.VIEWS it will obviously return my a list of views in my current database. In the view_definition column I can see it has the scripts to create the views.
Would I would like to know is how to loop through the results from (select * FROM INFORMATION_SCHEMA.VIEWS) and execute the scripts in the view_definition field? I understand this may not be the best practise however I would just like to learn how you would do such a thing.
You could try opening a cursor and creating the view for each row :
USE [Target DB];
DECLARE #view VARCHAR(MAX)
DECLARE curs CURSOR
FOR SELECT VIEW_DEFINITION FROM [Source DB].INFORMATION_SCHEMA.VIEWS
OPEN curs
FETCH NEXT FROM curs
INTO #view
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC(#view)
FETCH NEXT FROM curs
INTO #view
END
CLOSE curs
DEALLOCATE curs

Best way to replace cursor in SQL Server 2008

I want to replace the cursor code from my stored procedure
DECLARE CursorXD CURSOR FOR
SELECT
IDOrdre, Quantity,fabnum
FROM prod_ordreplanificationdetail
WHERE fab = #num
AND ordre = #ord
OPEN CursorXD
FETCH NEXT FROM CursorXD INTO #correctionnumsap, #correctionquantite, #correctionnumfabrication
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC INSERT#prod #idordre = #correctionnumsap,
#quantite = #correctionquantiteneg,
#fabnum = #correctionnumfabrication
FETCH NEXT FROM CursorXD INTO #correctionnumsap, #correctionquantite, #correctionnumfabrication
END
CLOSE CursorXD
DEALLOCATE CursorXD
What is the best way to replace this cursor to increase the performance??
Any suggestion?
Here's a option, but I made a couple of assumptions
INSERT INTO prod
SELECT IDOrdre, Quantity,fabnum
FROM prod_ordreplanificationdetail
WHERE fab=#num
AND ordre=#ord
Assumptions:
SP INSERT#prod only does an INSERT and no other data manipulation
SP INSERT#prod inserts into a table called prod and there are only three columns in the table
You can use WHILE loop instead of CURSOR by maintaining the nth row value. It is too good performance wise compare to CURSOR. If you tell clearly what you need, so we can walk into same road.

REPLACE in Sql replaces more than once issue

Weird not sure if I have it correct but the output is weird:
select *
into #TempScormModuelsTable
from Scorm.ScormModules
select * from #TempScormModuelsTable
DECLARE #ScormModuleId int
DECLARE db_cursor CURSOR FOR
SELECT ScormModuleId from #TempScormModuelsTable
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #ScormModuleId
WHILE ##FETCH_STATUS = 0
BEGIN
update #TempScormModuelsTable
set Directory = REPLACE(Directory,'SCORM','ScormPackages'),
RelativeHtmlPath = REPLACE(RelativeHtmlPath,'SCORM','ScormPackages')
FETCH NEXT FROM db_cursor INTO #ScormModuleId
END
--drop table #TempScormModuelsTable
CLOSE db_cursor
DEALLOCATE db_cursor
then in the column Directory I have this!
E:\inetpub\www.sellandprosper.com\ScormPackagesPackagesPackagesPackagesPackagesPackagesPackagesPackages\SellingOfficeWithNewPCs
"ScormPackagesPackagesPackagesPackagesPackagesPackagesPackagesPackages" was "SCORM" and I just want it as ScormPackages..yikes
any help?
That's not SQL.
You need something closer to...
UPDATE
Scorm.ScormModules_backup
SET
Directory = REPLACE(Directory, 'SCORM', 'ScormPackages'),
RelativeHtmlPath = REPLACE(RelativeHtmlPath, 'SCORM', 'ScormPackages')
SQL is already set based and this will naturally apply this to every row (much like your FOREACH pseudocode).
I'm not sure what your WHERE clause is meant to do, but you can append WHERE clauses to UPDATEs too.
EDIT
Okay you just totally changed the code in your question.
Now, you're looking in a cursor, but the update is being done to the whole table.
So, if you have 8 rows, you do the update 8 times. But each time you do it to every row in the table.

How to avoid duplicated FETCH in T-SQL when using a cursor?

In T-SQL, when iterating results from a cursor, it seems to be common practice to repeat the FETCH statement before the WHILE loop. The below example from Microsoft:
DECLARE Employee_Cursor CURSOR FOR
SELECT EmployeeID, Title FROM AdventureWorks2012.HumanResources.Employee
WHERE JobTitle = 'Marketing Specialist';
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor;
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM Employee_Cursor;
END;
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
GO
(Notice how FETCH NEXT FROM Employee_Cursor; appears twice.)
If the FETCH selects into a long list of variables, then we have a large duplicated statement which is both ugly and of course, "non-DRY" code.
I'm not aware of a post-condition control-of-flow T-SQL statement so it seems I'd have to resort to a WHILE(TRUE) and then BREAK when ##FETCH_STATUS is not zero. This feels clunky to me.
What other options do I have?
There's a good structure posted online by Chris Oldwood which does it quite elegantly:
DECLARE #done bit = 0
WHILE (#done = 0)
BEGIN
-- Get the next author.
FETCH NEXT FROM authors_cursor
INTO #au_id, #au_fname, #au_lname
IF (##FETCH_STATUS <> 0)
BEGIN
SET #done = 1
CONTINUE
END
--
-- stuff done here with inner cursor elided
--
END
This is what I've resorted to (oh the shame of it):
WHILE (1=1)
BEGIN
FETCH NEXT FROM C1 INTO
#foo,
#bar,
#bufar,
#fubar,
#bah,
#fu,
#foobar,
#another,
#column,
#in,
#the,
#long,
#list,
#of,
#variables,
#used,
#to,
#retrieve,
#all,
#values,
#for,
#conversion
IF (##FETCH_STATUS <> 0)
BEGIN
BREAK
END
-- Use the variables here
END
CLOSE C1
DEALLOCATE C1
You can see why I posted a question. I don't like how the control of flow is hidden in an if statement when it should be in the while.
The first Fetch shouldn't be a Fetch next, just a fetch.
Then you're not repeating yourself.
I'd spend more effort getting rid of the cursor, and less on DRY dogma, (but if it really matters, you could use a GOTO :) - Sorry, M. Dijkstra)
GOTO Dry
WHILE ##FETCH_STATUS = 0
BEGIN
--- stuff here
Dry:
FETCH NEXT FROM Employee_Cursor;
END;
Here is my humble contribution. Single FETCH statement, no GOTO, no BREAK, no CONTINUE.
-- Sample table
DROP TABLE IF EXISTS #tblEmployee;
CREATE TABLE #tblEmployee(ID int, Title varchar(100));
INSERT INTO #tblEmployee VALUES (1, 'First One'), (2, 'Second Two'), (3, 'Third Three'), (3, '4th Four');
-- Cursor with one FETCH statement
DECLARE #bEOF bit=0, #sTitle varchar(200), #nID int;
DECLARE cur CURSOR LOCAL FOR SELECT ID, Title FROM #tblEmployee;
OPEN cur;
WHILE #bEOF=0
BEGIN
FETCH NEXT FROM cur INTO #nID, #sTitle;
IF ##FETCH_STATUS<>0
SET #bEOF=1;
ELSE
BEGIN
PRINT Str(#nID)+'. '+#sTitle;
END;
END;
CLOSE cur;
DEALLOCATE cur;
-- Cleanup
DROP TABLE IF EXISTS #tblEmployee;
It is obvious that a cursor is the pointer to the current row in the recordset. But mere pointing isn't gonna make sense unless it can be used. Here comes the Fetch statement into the scene. This takes data from the recordset, stores it in the variable(s) provided. so if you remove the first fetch statement the while loop won't work as there is not "FETCHED" record for manipulation, if you remove the last fetch statement, the "while" will not loop-through.
So it is necessary to have both the fetch statement to loop-through the complete recordset.
Simply said you can't... that's just how most where statements in SQL work. You need to get the first line before the loop and then do it again in the while statement.
The better question how to get rid of the cursor and try to solve your query without it.

FOR UPDATE cursor is returning a result set for each row!

I'm using an UPDATE cursor as follows on SQL 2005:
DECLARE myCursor CURSOR FOR
SELECT RowID, Value FROM myTable
FOR UPDATE OF Value;
OPEN myCursor;
FETCH NEXT FROM myCursor
WHILE (##FETCH_STATUS <> -1)
UPDATE myTable SET Value = 42
WHERE CURRENT OF myCursor
FETCH NEXT FROM myCursor
END
CLOSE myCursor
DEALLOCATE myCursor
(Thanks to Matt for his correct answer on my prior question concerning this cursor syntax. And yes, I do need a cursor, because each row's new value is actually based on a complicated calculation that depends on the prior rows.)
This works correctly, updating all the Values. The problem is that it returns a result set for each row updated, consisting of RowID, Value (interestingly, its showing the result from before the row is updated). Eventually, I get the following error:
The query has exceeded the maximum
number of result sets that can be
displayed in the results grid. Only
the first 100 result sets are
displayed in the grid.
Any way to suppress these result sets? SET NOCOUNT ON doesn't do the trick.
Is this just an issue I see running it directly in SSMS? Or will it actually try to return hundreds of result sets when I put this cursor inside a stored proc?
EDIT: Looks like it has nothing to do with the UPDATE.
Using FETCH NEXT FROM myCURSOR the way I am actually does return a result set of the next row from the cursor.
If I change it to FETCH NEXT FROM myCURSOR INTO #variables, then it doesn't return a result set.
So I guess the question now is: Since I'm using WHERE CURRENT OF, I don't really need the variable. I guess I can put them in just to suppress the result set, but is there a better way to do it?
Note while begin ... end
and Fetch into
Declare #row int
Declare #value int
DECLARE myCursor CURSOR FOR
SELECT RowID, Value FROM myTable
FOR UPDATE OF Value;
OPEN myCursor;
FETCH NEXT FROM myCursor into #row, #value
WHILE (##FETCH_STATUS <> 1)
begin
UPDATE myTable SET Value = 42
WHERE CURRENT OF myCursor
FETCH NEXT FROM myCursor into #row, #value
END
CLOSE myCursor
DEALLOCATE myCursor