Cursor is repeating first record forever - sql-server

can someone tell me what is wrong with my code please. I'm simply trying to loop through a table with 2 records and get it to return 2 records. But as you may see in the image below, it keeps just repeating the first record (forever, until I hit cancel). Thank you
SET NOCOUNT ON -- Improves performance by not returning number of rows affected
--General Variables
DECLARE #ImportGUID uniqueidentifier =NEWID() -- Declares and sets a new Unique number. Can be used to remove records at a later stage
--Cursor Variables
DECLARE #FirstNameVariable varchar(50)
DECLARE #SurnameVariable varchar(50)
PRINT 'Starting import ' + CONVERT(varchar(255), #ImportGUID); --just display this on the screen
--Declare the first cursor which will loop through a table collecting data
DECLARE NewPersonTableImportCursor CURSOR FOR
SELECT Firstname, Surname
from dbo.A_NewPersonTable
--Open NewPersonTableImportCursor
OPEN NewPersonTableImportCursor
--Start looping through the data and updating the cursor variables with data from this cursor
FETCH NEXT FROM NewPersonTableImportCursor INTO #FirstNameVariable, #SurnameVariable
WHILE ##FETCH_STATUS=0 --Fetch Status 0 means successfull so only proceed on those rows from source table that were successfull
BEGIN
PRINT #FirstNameVariable;
END
CLOSE NewPersonTableImportCursor

Add FETCH NEXT FROM NewPersonTableImportCursor INTO #FirstNameVariable, #SurnameVariable before the end of the loop to get the next record

Related

Trouble getting value from stored procedure temp table column using cursor

First I know that using a cursor is not the best way of doing this but the IT manager is an old SQL person and that is the way he wants it done, otherwise I wouldn't be doing it this way.
I am using a stored procedure that does among other things create a temp table and try to fill it with data from another table that has been concatenated in the stored procedure. I can not seem to get the problem description, the field that needs to be concatenated, to update correctly. Actually not all.
Here is the part of the stored procedure that builds the temp table and tries to update it.
--Build problem entry
Create Table ##tmp_problem
(
prbqarnum varchar(7),
prbdesc varchar(max)
)
--Dump problem(s) into tbl based on qar#
Insert Into ##tmp_problem(prbqarnum) Select qarnum From tbl_qarbase Where currstatus = #qarstatus
--Declare tbl cursor
Declare tbl_Cursor CURSOR For
Select tbl_problems.qarnum, tbl_problems.problemdesc
From tbl_problems
Join tbl_qarbase On tbl_problems.qarnum = tbl_qarbase.qarnum
Where tbl_qarbase.currstatus = #qarstatus
--Open the tbl cursor
Open tbl_Cursor
--Fetch first row of data
Fetch next From tbl_Cursor Into #qarparm, #desc
--Declare temp problem desc variable
Declare #tmpproblem varchar(max)
--Loop to get problem data
While ##FETCH_STATUS = 0
Begin
Set #tmpproblem = (Select prbdesc From ##tmp_problem Where prbqarnum = #qarparm) + ' ' + #desc
Update ##tmp_problem Set prbdesc = #tmpproblem
--Get Next Row of Data
Fetch next From tbl_Cursor Into #qarparm, #desc
End
--Close tbl cursor
Close tbl_Cursor
--Deallocate tbl cursor
Deallocate tbl_Cursor
I know that the temp table is working because after the procedure runs i am able to query the temp table and see that the qarnum's are being put in.
What isn't happening is that there is a description field, that may have anywhere from one to N lines, and based on the qar # I need to concatenate the descriptions into one string and then insert it into the temp table, which isn't happening.
Here is a picture on the queries out puts. The top is the temp table and the bottom is the table that the cursor is built on.
So the question is, besides using a cursor, what i am doing wrong?? I have been Googling for several hours but nothing seems to work.
One last note: I am not seeing any errors anywhere.
You could GREATLY simplify this. There is no need for an update at all. Just populate both columns in your temp table. You entire code could be reduced to this.
Create Table #tmp_problem
(
prbqarnum varchar(7),
prbdesc varchar(max)
)
--Dump problem(s) into tbl based on qar#
Insert Into #tmp_problem(prbqarnum, prbdesc) Select qarnum, problemdesc From tbl_qarbase Where currstatus = #qarstatus
You should handle NULLs:
Set #tmpproblem = ISNULL(Select prbdesc From ##tmp_problem
Where prbqarnum = #qarparm), '') + ' '
+ ISNULL(#desc, '');
Plus you need to add some condition to UPDATE
Update ##tmp_problem Set prbdesc = #tmpproblem -- updates entire table
--WHERE ...

SQL Server - Triggers & Cursors - For Each Inserted Row, Update an Associated Value on Another Table

For a homework assignment, I'm trying to build a trigger that allows for multiple inserts/updates/deletes by utilizing a cursor. We have to use a cursor in order to practice the syntax. We know that there are very few practical scenarios for cursors in a production environment.
Here's what I'm trying to accomplish:
For each row inserted into the TAL_ORDER_LINE table, update the ON_HAND value in the TAL_ITEM table by subtracting the NUM_ORDERED value from the stored ON_HAND value.
Table Structure:
Current Query:
ALTER TRIGGER update_on_hand
ON TAL_ORDER_LINE
AFTER INSERT AS
DECLARE #vItemNum as char
DECLARE #vNumOrdered as int
DECLARE new_order CURSOR FOR
SELECT ITEM_NUM, NUM_ORDERED
FROM inserted
OPEN new_order;
FETCH NEXT FROM new_order INTO #vItemNum, #vNumOrdered;
WHILE ##FETCH_STATUS=0
BEGIN
UPDATE TAL_ITEM
SET ON_HAND = ON_HAND - #vNumOrdered
WHERE ITEM_NUM = #vItemNum
FETCH NEXT FROM new_order INTO #vItemNum, #vNumOrdered;
END
CLOSE new_order
DEALLOCATE new_order
My Insert Query:
INSERT INTO TAL_ORDER_LINE (ORDER_NUM, ITEM_NUM, NUM_ORDERED, QUOTED_PRICE)
VALUES (51626, 'KL78', 10, 10.95), (51626, 'DR67', 10, 29.95)
It runs successfully, but does not affect the ON_HAND value. I think the biggest problem is that I'm struggling to understand cursor syntax, especially the INTO clause in the FETCH statement and how data from the 'inserted' table is passed into the cursor. What do I need to know to get this to work? Thanks in advance!
Your problem is likely due to this:
DECLARE #vItemNum as char
it is HIGHLY unlikely that the ItemNum column is a single character. For future reference, you should always verify that you variable definitions are consistent with the values you expect to store in them. And as has been hinted - you will get better answers by posting a complete script rather than a picture.
Big question,how you gonna debug ?
Is On_Hand col NULL , then do this isnull(on_Hand,0)
DECLARE #vItemNum as char
DECLARE #vNumOrdered as int
DECLARE new_order CURSOR FOR
SELECT ITEM_NUM, NUM_ORDERED
FROM TAL_ORDER_LINE
OPEN new_order;
FETCH NEXT FROM new_order INTO #vItemNum, #vNumOrdered;
WHILE ##FETCH_STATUS=0
BEGIN
--UPDATE TAL_ITEM
--SET ON_HAND = ON_HAND - #vNumOrdered
--WHERE ITEM_NUM = #vItemNum
print #vItemNum
print vNumOrdered
FETCH NEXT FROM new_order INTO #vItemNum, #vNumOrdered;
END
CLOSE new_order
DEALLOCATE new_order
Try this :
ALTER TRIGGER update_on_hand ON TAL_ORDER_LINE
FOR INSERT AS
BEGIN
UPDATE TI
SET TI.ON_HAND = TI.ON_HAND - I.NUM_ORDERED
TAL_ITEM TI INNER JOIN
INSERTED I ON I.ITEM_NUM = TI.ITEM_NUM
END
Changed Trigger to FOR INSERT Trigger
Removed Cursor
Note: NOT Tested. ( If you post the sql scripts for create table + sample inserts I can give it a try )

Assign result of SELECT * statement to variable SQL SERVER

I have a table with 700 rows. What I want to do is, to execute `select * from table_name' query on it and whatever result I will get want to store it in a variable and after that is done, want to traverse through each record for processing purpose? How do I achieve it? Any help??
Thanks in adv,
-saurabh
you want something which is called cursors
Cursors
You use a cursor to fetch rows returned by a query. You retrieve the rows into the cursor using a query and then fetch the rows one at a time from the cursor.
Steps
Declare variables to store the column values for a row.
Declare the cursor, which contains a query.
Open the cursor.
Fetch the rows from the cursor one at a time and store the column values in the variables declared in Step 1. You would then do something with those variables; such as display them on the screen, use them in a calculation, and so on.
Close the cursor.  
hopefully this might help you cursor
here is an example I use to start with
USE pubs
GO
-- Declare the variables to store the values returned by FETCH.
DECLARE #au_lname varchar(40), #au_fname varchar(20)
DECLARE authors_cursor CURSOR FOR
SELECT au_lname, au_fname FROM authors
--WHERE au_lname LIKE 'B%'
ORDER BY au_lname, au_fname
OPEN authors_cursor
-- Perform the first fetch and store the values in variables.
-- Note: The variables are in the same order as the columns
-- in the SELECT statement.
FETCH NEXT FROM authors_cursor
INTO #au_lname, #au_fname
-- Check ##FETCH_STATUS to see if there are any more rows to fetch.
WHILE ##FETCH_STATUS = 0
BEGIN
-- Concatenate and display the current values in the variables.
PRINT #au_fname
-- This is executed as long as the previous fetch succeeds.
FETCH NEXT FROM authors_cursor
INTO #au_lname, #au_fname
END
CLOSE authors_cursor
DEALLOCATE authors_cursor
GO

Table Variable inside cursor, strange behaviour - SQL Server

I observed a strange thing inside a stored procedure with select on table variables. It always returns the value (on subsequent iterations) that was fetched in the first iteration of cursor. Here is some sample code that proves this.
DECLARE #id AS INT;
DECLARE #outid AS INT;
DECLARE sub_cursor CURSOR FAST_FORWARD
FOR SELECT [TestColumn]
FROM testtable1;
OPEN sub_cursor;
FETCH NEXT FROM sub_cursor INTO #id;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Log TABLE (LogId BIGINT NOT NULL);
PRINT 'id: ' + CONVERT (VARCHAR (10), #id);
INSERT INTO Testtable2 (TestColumn)
OUTPUT inserted.[TestColumn] INTO #Log
VALUES (#id);
IF ##ERROR = 0
BEGIN
SELECT TOP 1 #outid = LogId
FROM #Log;
PRINT 'Outid: ' + CONVERT (VARCHAR (10), #outid);
INSERT INTO [dbo].[TestTable3] ([TestColumn])
VALUES (#outid);
END
FETCH NEXT FROM sub_cursor INTO #id;
END
CLOSE sub_cursor;
DEALLOCATE sub_cursor;
However, while I was posting the code on SO and tried various combinations, I observed that removing top from the below line, gives me the right values out of table variable inside a cursor.
SELECT TOP 1 #outid = LogId FROM #Log;
which would make it like this
SELECT #outid = LogId FROM #Log;
I am not sure what is happening here. I thought TOP 1 on table variable should work, thinking that a new table is created on every iteration of the loop. Can someone throw light on the table variable scoping and lifetime.
Update: I have the solution to circumvent the strange behavior here.
As a solution, I have declared the table at the top before the loop and deleting all rows at the beginning of the loop.
There are numerous things a bit off with this code.
First off, you roll back your embedded transaction on error, but I never see you commit it on success. As written, this will leak a transaction, which could cause major issues for you in the following code.
What might be confusing you about the #Log table situation is that SQL Server doesn't use the same variable scoping and lifetime rules as C++ or other standard programming languages. Even when declaring your table variable in the cursor block you will only get a single #Log table which then lives for the remainder of the batch, and which gets multiple rows inserted into it.
As a result, your use of TOP 1 is not really meaningful, since there's no ORDER BY clause to impose any sort of deterministic ordering on the table. Without that, you get whatever order SQL Server sees fit to give you, which in this case appears to be the insertion order, giving you the first inserted element of that log table every time you run the SELECT.
If you truly want only the last ID value, you will need to provide some real ordering criterion for your #Log table -- some form of autonumber or date field alongside the data column that can be used to provide the proper ordering for what you want to do.

Delete row from cursor source SQL Server

I have a SQL Server 2005 cursor operating over a table variable called #workingSet.
Some times rows can be related and in this case I process the row I have fetched and the related rows at the same time. I then remove the related records from #workingset as I don't need to process then in the loop.
In a #workingSet with 7 rows, the first two are related so when I process 1 I also process 2. I remove row 2 from the cursor source (#workingSet) and then fetch next. The problem is it returns the second row in #workingset (the one I deleted on the previous iteration).
I was of the impression that this could be done i.e. deleting an item from a source that a cursor operates on and it will honour the delete in subsequent fetches.
The answer appears to be that the table variable that is being used as the source of the cursor needs to have a primary key. I've added this and all works correctly.
Not massively familiar with cursors but from a quick test this end you need to avoid declaring the cursor with the STATIC or KEYSET options then the changes to the underlying table are reflected in the cursor.
SET NOCOUNT ON;
DECLARE #WorkingTable TABLE(C int)
INSERT INTO #WorkingTable VALUES (1),(2),(3)
DECLARE #C int
DECLARE wt_cursor CURSOR
DYNAMIC /*Or left blank but not STATIC or KEYSET*/
FOR
SELECT C
FROM #WorkingTable
OPEN wt_cursor;
FETCH NEXT FROM wt_cursor
INTO #C
DELETE FROM #WorkingTable
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #C;
FETCH NEXT FROM wt_cursor
INTO #C;
END
CLOSE wt_cursor;
DEALLOCATE wt_cursor;

Resources