Cursor not incrementing updating row with start value - sql-server

I'm very new to SQL Server. I'm using a cursor to populate a table with ids; I just discovered cursors today. The code is running but it is populating each row with the start value.
SET NOCOUNT ON
DECLARE #Irow int
declare #cheese int;
set #cheese = (select (max(balanceid) + 1) from balancetbl)
DECLARE aurum CURSOR FOR
SELECT #Irow
FROM aurumaugupload
OPEN aurum
FETCH aurum INTO #Irow
WHILE ##Fetch_Status = 0
BEGIN
update aurumaugupload set balanceid = #cheese
set #cheese = #cheese + 1;
FETCH aurum INTO #Irow
END
CLOSE aurum
DEALLOCATE aurum
RETURN
I think it's a really basic error but I can't see it due to my inexperience.
UPDATE: thanks guys for your prompts answers. I got it working after nonnb's help. Here's the final code:
SET NOCOUNT ON
DECLARE #acc int
declare #start int;
set #start = (select (max(balanceid) + 1) from balancetbl)
DECLARE aurum CURSOR FOR
SELECT accountid
FROM aurumaugupload
OPEN aurum
FETCH aurum INTO #acc
WHILE ##Fetch_Status = 0
BEGIN
update aurumaugupload set balanceid = #start where accountid = #acc
set #start = #start + 1;
FETCH aurum INTO #acc
END
CLOSE aurum
DEALLOCATE aurum
RETURN

There are at least 2 bugs here:
Bug 1
DECLARE aurum CURSOR FOR
SELECT #Irow
FROM aurumaugupload
will select the same (unitialised) constant for every row of aurumaugupload
You need something like
SELECT Irow
FROM aurumaugupload
Bug 2 - You are updating all rows within the cursor. You need a where
update aurumaugupload set balanceid = #cheese
where IRow = #IRow;
set #cheese = #cheese + 1

Your update statement doesn't have a where clause, so you are updating every row each time.

Try this solution (if the sorting/update order doesn't matter):
SET NOCOUNT ON
DECLARE #Irow int
DECLARE #cheese int;
SET #cheese = (SELECT (MAX(balanceid) ) FROM balancetbl)
UPDATE aurumaugupload
set #cheese = balanceid = #cheese+1;

Related

SQL Server Nested Cursors and Variables Declaration

I have a doubt regarding the variable declaration in a nested cursors scenario.
This is an small nested cursor sample that i found. In other samples I've seen I also find DECLARE clauses inside the first cursor.
DECLARE #ClientID int;
DECLARE Cur1 CURSOR FOR SELECT ClientID From Folder;
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #ClientID;
SELECT #FETCH_Cur1 = ##FETCH_STATUS
WHILE #FETCH_Cur1 = 0
BEGIN
DECLARE #UID int;
DECLARE Cur2 CURSOR FOR SELECT UID FROM Attend Where ClientID=#ClientID;
OPEN Cur2;
FETCH NEXT FROM Cur2 INTO #UID;
SELECT #FETCH_Cur2 = ##FETCH_STATUS
WHILE #FETCH_Cur2 = 0
BEGIN
PRINT 'Found UID: ' + Cast(#UID as Varchar);
FETCH NEXT FROM Cur2 INTO #UID;
SELECT #FETCH_Cur2 = ##FETCH_STATUS
END;
CLOSE Cur2;
DEALLOCATE Cur2;
FETCH NEXT FROM Cur1 INTO #ClientID;
SELECT #FETCH_Cur1 = ##FETCH_STATUS
END;
PRINT 'DONE';
CLOSE Cur1;
DEALLOCATE Cur1;
The code works, but my doubt is if it's correct the DECLARATIONS inside the first cursor.
DECLARE #UID int;
Shouldn't Declarations be placed at the beginning of code, as is normally done for other programming languages?
You can DECLARE a variable inside a WHILE, yes; the latter DECLAREs will simply be ignored. If you declared the variable and assigned it a value at the time (for example DECLARE #UID int = 1; it would be assigned 1 in each iteration:
DECLARE #I int = 1;
WHILE #i < 10 BEGIN
DECLARE #W int;
SET #W = ISNULL(#W,1) + 1;
SET #I = #I + 1
END
SELECT #W; --10
GO
DECLARE #I int = 1;
WHILE #i < 10 BEGIN
DECLARE #W int = 0;
SET #W = ISNULL(#W,1) + 1;
SET #I = #I + 1
END
SELECT #W; -- 1
DB<>fiddle
Of course, I personally prefer to DECLARE the variables outside of the WHILE as I feel the code is "cleaner", but that doesn't mean you have to.

Don't know how to use varchar variable in select clause of query

I''m trying to use a varchar variable in my select, but its registering as a string and outputting just the string itself instead of treating it like an actual column.
Not very many solutions online, as it seems like this isn't a problem very many run into.
Declare #counter INT = 0
Declare #totalcol INT
Declare #col VARCHAR(50)
select #totalcol = count(*)
FROM [Loyalty_DW].information_schema.columns
WHERE table_name = 'Transactions'
while (#counter < #totalcol)
begin
select #col = COLUMN_NAME
from [Loyalty_DW].information_schema.columns
where table_name = 'Transactions'
order by (select null)
offset #counter rows
fetch next 1 rows only
select distinct(#col)
from [Loyalty_DW].dbo.Transactions
set #counter += 1
end
The output is just a string with no actual data returned. The same as if I were to say select 'asdf' from tablename where ... it would just output 'asdf'.
You can use CURSOR statement rather than WHILE statement, as Gordon says you need to use dynamic SQL:
DECLARE #columName AS NVARCHAR(50);
DECLARE #sqlText AS NVARCHAR(1000);
DECLARE cursor_name CURSOR FOR
SELECT COLUMN_NAME
FROM [Loyalty_DW].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Transactions'
ORDER BY(SELECT NULL)
OPEN cursor_name
FETCH NEXT FROM cursor_name INTO #columName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlText = N'SELECT DISTINCT ' + #columName + ' FROM [Loyalty_DW].dbo.Transactions'
EXEC (#sqlText)
FETCH NEXT FROM cursor_name INTO #columName
END
CLOSE cursor_name
DEALLOCATE cursor_name

Update statement not working within Cursor & While block

I am trying to execute an Update statement on a table. This statement is placed within Cursor and While block. I have checked in debugger and the values are coming into the statements and variable, still the update is not putting values into the table fields. Please advise what am I doing wrong here.
ALTER PROCEDURE SP_PO1 #P1 int
AS
BEGIN
SET NOCOUNT ON;
DECLARE #DOC AS INT;
DECLARE #CASH AS FLOAT;
DECLARE #TENTYPE AS VARCHAR(100);
DECLARE #UDF AS VARCHAR(100);
DECLARE #COUNTER AS INT;
DECLARE #SQL AS VARCHAR(500);
SELECT #DOC=DOCTYPE FROM InvNum WHERE AutoIndex = #P1;
IF #DOC = 6
BEGIN
SET #COUNTER = 1;
DECLARE Cur_Tender CURSOR FOR
SELECT Tender.TenderNo FROM Tender;
OPEN CUR_TENDER;
FETCH NEXT FROM CUR_TENDER INTO #TENTYPE;
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #CASH = ISNULL(_btblPOSTenderTx.fTxAmount,0) FROM _btblPOSTenderTx INNER JOIN Tender ON _btblPOSTenderTx.iTenderID = Tender.IdTender INNER JOIN _btblPOSXZTable ON _btblPOSTenderTx.iPOSXZTableID = _btblPOSXZTable.IDPOSXZTable WHERE (_btblPOSXZTable.iTillTxType = 7) and (_btblPOSXZTable.IDPOSXZTable = (select Max(IDPOSXZTable) from [dbo].[_btblPOSXZTable])) AND (TenderNo = #TENTYPE);
SET #UDF = 'ufIDPOSInvTENDER' + CONVERT(VARCHAR(2),#COUNTER);
UPDATE InvNum SET #UDF=#CASH WHERE AutoIndex = #P1;
SET #COUNTER = #COUNTER + 1;
FETCH NEXT FROM CUR_TENDER INTO #TENTYPE;
END
END
CLOSE CUR_TENDER
DEALLOCATE CUR_TENDER
END
GO
You update variable
UPDATE InvNum SET #UDF=#CASH WHERE AutoIndex = #P1;
update table column
UPDATE InvNum SET <column>=#CASH WHERE AutoIndex = #P1;
if you want a dynamic column name - use dynamic sql
EXEC('UPDATE InvNum SET ' + #UDF + '=' + CAST(#CASH as VARCHAR(50) + ' WHERE AutoIndex = ' + CAST(#P1 as VARCHAR(5) ' );

function with cursor in sql server

I have written a SQL scalar function with a cursor to retrieve item names with comma separated values. This is my function but it gives me an error.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[GET_ITEM_NAME]
(
#PRINT_RECEIPT_MST_ID INT
)
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #ITEM_TITLE VARCHAR(1000)
DECLARE #ITEM VARCHAR(100)
SET #ITEM_TITLE = '';
DECLARE ItemTitleCursor CURSOR FAST_FORWARD
FOR
SELECT ISNULL(IMT.ITEM_TITLE,'')
FROM PRINT_RECEIPT_DET_T PDT
LEFT OUTER JOIN ITEM_MST_T IMT ON IMT.ITEM_ID=PDT.ITEM_ID
WHERE PDT.PRINT_RECEIPT_MST_ID = #PRINT_RECEIPT_MST_ID
OPEN ItemTitleCursor
FETCH NEXT FROM ItemTitleCursor INTO #ITEM
WHILE ##FETCH_STATUS = 0
BEGIN
IF ( #ITEM_TITLE = '' )
BEGIN
SET #ITEM_TITLE = CAST(#ITEM AS VARCHAR(100));
END
ELSE
BEGIN
SET #ITEM_TITLE = CAST(#ITEM_TITLE AS VARCHAR(1000))
+ ' , ' + CAST(#ITEM AS VARCHAR(100));
END
FETCH NEXT FROM ItemTitleCursor INTO #ITEM
END
CLOSE ItemTitleCursor
DEALLOCATE ItemTitleCursor
RETURN #ITEM_TITLE
END
If am i am not wrong this should replace your cursor
DECLARE #ITEM_TITLE VARCHAR(1000)='',
#PRINT_RECEIPT_MST_ID INT
SELECT #ITEM_TITLE = + Isnull(IMT.ITEM_TITLE, '') + ','
FROM PRINT_RECEIPT_DET_T PDT
LEFT OUTER JOIN ITEM_MST_T IMT
ON IMT.ITEM_ID = PDT.ITEM_ID
WHERE PDT.PRINT_RECEIPT_MST_ID = #PRINT_RECEIPT_MST_ID
SELECT #Left(#ITEM_TITLE, Len(#ITEM_TITLE) - 1)
or Use For xml path to convert all the rows to csv
DECLARE #ITEM_TITLE VARCHAR(1000)='',
#PRINT_RECEIPT_MST_ID INT
SET #ITEM_TITLE=(SELECT Isnull(IMT.ITEM_TITLE, '') + ','
FROM PRINT_RECEIPT_DET_T PDT
LEFT OUTER JOIN ITEM_MST_T IMT
ON IMT.ITEM_ID = PDT.ITEM_ID
WHERE PDT.PRINT_RECEIPT_MST_ID = #PRINT_RECEIPT_MST_ID
FOR XML PATH(''))
SELECT Left(#ITEM_TITLE, Len(#ITEM_TITLE) - 1)
Thank you all for your quick responses!
i have solved it.my cursor returns length more than i had mentioned.it should have been
RETURNS VARCHAR(4000)

Need to repeat the result rows based on a column value

I need a help in generating SQL result based on a column value to calculate per day amount.
I have a SQL like below.
select guid, reccharge, dayCareDaysCharged, dayCareHoursPerDay, RATE from tableA.
Here if reccharge = 0 (weekly) then the result should have 7 rows with RATE as RATE/7 for each row (per day amount);
if reccharge = 1 (monthly) then the result should have 30 rows with RATE as RATE/30 for each row;
if reccharge = 2 (yearly) then the result should have 365 rows with RATE as RATE/365 for each row;
if reccharge = 3 (hourly) then there should be a row as calculate it for a day;
How can I achieve this. Please help.
The question seems a little strange to me, but if I've followed it correctly then maybe this;
CREATE TABLE [#temp1] (reccharge tinyint,rdays smallint)
GO
declare #rcount smallint
set #rcount=0
while #rcount<7
begin
set #rcount=#rcount+1
insert #temp1 values (0,7)
end
set #rcount=0
while #rcount<30
begin
set #rcount=#rcount+1
insert #temp1 values (1,30)
end
set #rcount=0
while #rcount<365
begin
set #rcount=#rcount+1
insert #temp1 values (2,365)
end
insert #temp1 values (3,1)
GO
select t1.[guid], t1.reccharge, t1.dayCareDaysCharged, t1.dayCareHoursPerDay, t1.RATE/t2.rdays as RATE from tableA t1
inner join #temp1 t2 on t1.reccharge=t2.reccharge
order by t1.[guid]
I have not idea to do it in single query, but you can do it by using CURSOR as like below :
declare #reccharge int
declare #count int
declare #i int
declare #strSQL nvarchar(max) = 'select 1, 1 where 1 <> 1'
declare CurTest cursor for select reccharge from test
open CurTest
fetch next from CurTest into #reccharge
while ##fetch_status = 0
begin
set #i = 0;
select #count = case when #reccharge = 0 then 7 when #reccharge = 1 then 30 when #reccharge = 2 then 365 else 1 end
while #i < #count
begin
set #strSQL = #strSQL + ' union all select reccharge, Rate/' + cast(#count as varchar(10)) + ' from test where reccharge = ' + cast(#reccharge as varchar(10))
set #i = #i + 1;
end
fetch next from CurTest into #reccharge
end
close CurTest
deallocate CurTest
exec(#strSQL)

Resources