6

Before I go any further: Yes, I know that cursors perform poorly compared with set-based operations. In this particular case I'm running a cursor on a temporary table of 100 or so records, and that temporary table will always be fairly small, so performance is less crucial than flexibility.

My difficulty is that I'm having trouble finding an example of how to update a column fetched by a cursor. Previously when I've used cursors I've retrieved values into variables, then run an update query at each step based upon these values. On this occasion I want to update a field in the temporary table, yet I can't figure out how to do it.

In the example below, I'm trying to update the field CurrentPOs in temporary table #t1, based upon a query that uses #t1.Product_ID to look up the required value. You will see in the code that I have attempted to use the notation curPO.Product_ID to reference this, but it doesn't work. I have also attempted to use an update statement against curPO, also unsuccessfully.

I can make the code work by fetching to variables, but I'd like to know how to update the field directly.

I think I'm probably missing something obvious, but can anyone help?

declare curPO cursor
for select Product_ID, CurrentPOs from #t1
for update of CurrentPOs
open curPO

fetch next from curPO

while @@fetch_status = 0
begin
    select      OrderQuantity = <calculation>,
                ReceiveQuantity = <calculation>
    into        #POs
    from        PurchaseOrderLine POL 
    inner join  SupplierAddress SA ON POL.Supplier_ID = SA.Supplier_ID
    inner join  PurchaseOrderHeader POH ON POH.PurchaseOrder_ID = POL.PurchaseOrder_ID
    where       Product_ID = curPO.Product_ID
    and         SA.AddressType = '1801'

    update curPO set CurrentPOs = (select sum(OrderQuantity) - sum(ReceiveQuantity) from #POs)

    drop table #POs

    fetch next from curPO
end

close curPO
deallocate curPO
OMG Ponies
  • 325,700
  • 82
  • 523
  • 502
Billious
  • 2,563
  • 4
  • 21
  • 26
  • 1
    Am I missing something, or could the cursor be replaced with an UPDATE statement? – OMG Ponies Sep 24 '09 at 06:40
  • I tried that, but the sections marked have aggregates in them that caused complications. I remember that it was to do with the fact that I was running an aggregate query on another aggregate query, but I can't remember why it didn't work now! Will leave another comment if it comes to mind. Regardless, I'd like to know how to do this with a cursor! – Billious Sep 24 '09 at 06:48
  • I remember now - the nested aggregate in the update kept coming up with "Incorrect syntax near ')'." Basically it wouldn't allow me to use an aggregate query as a subquery. – Billious Sep 24 '09 at 23:02

4 Answers4

13

After doing a bit more googling, I found a partial solution. The update code is as follows:

UPDATE #T1 
SET    CURRENTPOS = (SELECT SUM(ORDERQUANTITY) - SUM(RECEIVEQUANTITY) 
                     FROM   #POS) 
WHERE  CURRENT OF CURPO 

I still had to use FETCH INTO, however, to retrieve #t1.Product_ID and run the query that produces #POs, so I'd still like to know if it's possible to use FETCH on it's own.

Gidil
  • 4,137
  • 2
  • 34
  • 50
Billious
  • 2,563
  • 4
  • 21
  • 26
  • 1
    Updates of values in tables are done via update statements on tables. WHERE CURRENT OF cursor, will allow the update to happen using cursor state, instead of looking up the correct row, as with, update ... where key = @key. – Shannon Severance Sep 24 '09 at 18:41
  • Thanks for that - yes, basically that's what I figured out. WHERE CURRENT OF was the bit of syntax I hadn't found in the MS documentation, though I'm sure it's there somewhere. – Billious Sep 24 '09 at 23:03
3

Is this what you want?

declare curPO cursor
for select Product_ID, CurrentPOs from #t1
for update of CurrentPOs
open curPO

fetch next from curPO

while @@fetch_status = 0
begin
    update curPO set CurrentPOs =
      (select      sum(<OrderQuantityCalculation>)
       from        PurchaseOrderLine POL 
       inner join  SupplierAddress SA ON POL.Supplier_ID = SA.Supplier_ID
       inner join  PurchaseOrderHeader POH ON POH.PurchaseOrder_ID = POL.PurchaseOrder_ID
       where       Product_ID = curPO.Product_ID
       and         SA.AddressType = '1801') -
      (select      sum(<ReceiveQuantityCalculation>)
       from        PurchaseOrderLine POL 
       inner join  SupplierAddress SA ON POL.Supplier_ID = SA.Supplier_ID
       inner join  PurchaseOrderHeader POH ON POH.PurchaseOrder_ID = POL.PurchaseOrder_ID
       where       Product_ID = curPO.Product_ID
       and         SA.AddressType = '1801')

    fetch next from curPO
end

close curPO
deallocate curPO
Jeremy Stein
  • 19,171
  • 16
  • 68
  • 83
  • with a little more work, the cursor select could be included within update itself, removing that horrible loop – KM. Sep 24 '09 at 18:17
  • His comment on the question itself claims he couldn't make it work. Go for it! – Jeremy Stein Sep 24 '09 at 18:56
  • I haven't tried this version yet, but it looks like it will have the same problem I described above. I'm trying to make reference to a field in the cursor row using "curPO.Product_ID", however it returns "The multi-part identifier 'curPO.Product_ID' could not be bound." Obviously I'm using the wrong syntax. Is it possible to refer to a field in the current cursor row in this way at all? Or do you have to use FETCH INTO? – Billious Sep 24 '09 at 23:08
1

Maybe you need something like that:

update DataBaseName..TableName
set ColumnName = value
where current of your_cursor_name;
Pavel Kovalev
  • 7,521
  • 5
  • 45
  • 67
0

Here's an example to calculate one column based upon values from two others (note, this could be done during the original table select). This example can be copy / pasted into an SSMS query window to be run without the need for any editing.

DECLARE @cust_id INT = 2, @dynamic_val NVARCHAR(40), @val_a INT, @val_b INT

DECLARE @tbl_invoice table(Cust_ID INT, Cust_Fees INT, Cust_Tax INT)

INSERT @tbl_invoice ( Cust_ID, Cust_Fees, Cust_Tax ) SELECT 1, 111, 11
INSERT @tbl_invoice ( Cust_ID, Cust_Fees, Cust_Tax ) SELECT 2, 222, 22
INSERT @tbl_invoice ( Cust_ID, Cust_Fees, Cust_Tax ) SELECT 3, 333, 33

DECLARE @TblCust TABLE
(
    Rec_ID INT
    , Val_A INT
    , Val_B INT
    , Dynamic_Val NVARCHAR(40)
    , PRIMARY KEY NONCLUSTERED (Rec_ID)
)

INSERT @TblCust(Rec_ID, Val_A, Val_B, Dynamic_Val)
SELECT Rec_ID = Cust_ID, Val_A = Cust_Fees, Val_B = Cust_Tax, NULL
FROM @tbl_invoice

DECLARE cursor_cust CURSOR FOR
    SELECT Rec_ID, Val_A, Val_B, Dynamic_Val
    FROM @TblCust
    WHERE Rec_ID <> @cust_id
    FOR UPDATE OF Dynamic_Val;

OPEN cursor_cust;

FETCH NEXT FROM cursor_cust INTO @cust_id, @val_a, @val_b, @dynamic_val;

WHILE @@FETCH_STATUS = 0
BEGIN
    UPDATE @TblCust
        SET Dynamic_Val = N'@c = "' + LTRIM(STR((@val_a + @val_b), 40)) + N'"'
        WHERE CURRENT OF cursor_cust

    FETCH NEXT FROM cursor_cust INTO @cust_id, @val_a, @val_b, @dynamic_val;
END

CLOSE cursor_cust

DEALLOCATE cursor_cust

SELECT * FROM @TblCust
  • A link to a solution is welcome, but please ensure your answer is useful without it: [add context around the link](https://meta.stackexchange.com/a/8259) so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. [Answers that are little more than a link may be deleted](https://stackoverflow.com/help/deleted-answers). – ChrisMM Dec 13 '19 at 19:06
  • Hope this avoids having my answer deleted. :) – Joel R. Hall Dec 13 '19 at 20:59
  • Thanks, this was helpful – Andrea Jun 03 '21 at 19:35