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;
I have tried some solutions from the internet but they are not working for me .
My task is to get the out put of stored procedure into a table.The data is being inserted inside a cursor by loop . I am creating temporary table to store and display the data.
My code:
ALTER procedure [dbo].[usp_Test]
AS
SET NOCOUNT ON;
declare #caseId int;
declare #CHG_ID int;
declare #HEAR_ID int;
SET #CHG_ID = 1
set #testid = 1;
DECLARE db_cursor CURSOR FOR
SELECT C_CASE_ID
FROM table1 // tHERE WILL BE MULTIPLE CASEIDS
-- here I am trying to delete the temporary table, but it does not work
IF OBJECT_ID('tempdb..##test_temp_table') IS NOT NULL
TRUNCATE TABLE ##test_temp_table
ELSE
CREATE TABLE test_temp_table(HEAR_ID int)
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #caseId
WHILE ##FETCH_STATUS = 0
BEGIN
insert into test_temp_table
EXEC STOREDPROCTEST2 #caseId, 1, #HEAR_ID OUTPUT;
-- LOOP THROUGH THE CURSOR TO GET ALL CASE IDS
FETCH NEXT FROM db_cursor INTO #caseId
SELECT HEAR_ID FROM test_temp_table;
END
CLOSE db_cursor
DEALLOCATE db_cursor;
I have two issues:
I cannot delete the temporary table
I am not seeing any output from the temporary table
[##test_temp_table] and [test_temp_table] are two different tables. First one is a global temp table, second one is a user table. I believe you want to replace the user table with the global temp table, i.e. replace object [test_temp_table] with [##test_temp_table]. or vice versa. In the end, you have to ensure you are querying the correct table.
So I am using the tsql code to run through a bunch of servers, and look in each database for the users. My issue that on a specific server, there are databases I do not have access to and do not need to use. When the query runs on them it stops on the entire server and moves to the next one. I have been trying to find a way to exclude certain databses from the search.
What I am trying to do is
for example on server A excluded these databses B,C,D, and so on . I have tried where <> and != and does not work or I have the wrong syntax
USE MASTER
If OBJECT_ID('#TDB', 'U') > 0
Drop Table #TDB
DECLARE #dbname varchar(200),
#sql varchar(max)
CREATE TABLE #TDB (
DataBaseName nvarchar(200),
UserName nvarchar(200)
)
DECLARE db_cursor CURSOR FOR
SELECT name
FROM master.dbo.sysdatabases WHERE DBID>4
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #dbname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql='insert into #TDb(DataBaseName,UserName)
select '''+#dbname+''' DataBaseName,[user_name] UserName FROM '+#dbname+'.[dbo].[USERS] where'+#dbname+'<>[APSSWATCH]'
EXEC(#sql)
FETCH NEXT FROM db_cursor INTO #dbname
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT * FROM #TDB ORDER BY DataBaseName,UserName
DROP TABLE #TDB
DECLARE db_cursor CURSOR FOR
SELECT name
FROM master.dbo.sysdatabases WHERE DBID>4 and name<> 'APSSWATCH'
You can remove this one:
where'+#dbname+'<>[APSSWATCH]'
Thank you for leading me into the right direction though, instad of
name<>'APPSWATCH'
I used
name NOT IN 'APPSWATCH'
and it works
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.
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