开发者

How to commit inside a CURSOR Loop?

开发者 https://www.devze.com 2022-12-30 23:48 出处:网络
I am trying to see if its possible to perform Update within a cursor loop and this updated data gets reflected during the second iteration in the loop.

I am trying to see if its possible to perform Update within a cursor loop and this updated data gets reflected during the second iteration in the loop.

DECLARE cur CURSOR
FOR SELECT [Product], [Customer], [Date], [Event] FROM MyTable
WHERE [Event] IS NULL

OPEN cur
FETCH NEXT INTO @Product, @Customer, @Date, @Event
WHILE @@FETCH_STATUS = 0
BEGIN
  SELECT * FROM MyTable WHERE [Event] = 'No Event' AND [Date] < @DATE
  -- Now I update my Event value to 'No Event' for records whose date is less than @Date
  UPDATE MyTable SET [Event] = 'No Event' WHERE [Product]开发者_如何学运维 = @Product AND [Customer] = @Customer AND [Date] < @DATE
   FETCH NEXT INTO @Product, @Customer, @Date, @Event
END
CLOSE cur
DEALLOCATE cur

Assume when the sql executes the Event column is NULL for all records In the above sql, I am doing a select inside the cursor loop to query MyTable where Event value is 'No Event' but the query returns no value even though I am doing an update in the next line. So, I am thinking if it is even possible to update a table and the updated data get reflected in the next iteration of the cursor loop.

Thanks for any help, Javid


Firstly You shouldn't need a cursor here. Something like the following would have the same semantics (from a starting position where all Event are NULL) and be more efficient.

WITH T
     AS (SELECT [Event],
                RANK() OVER (PARTITION BY [Product], [Customer] 
                                 ORDER BY [Date] DESC) AS Rnk
         FROM   MyTable)
UPDATE T
SET    [Event] = 'No Event'
WHERE  Rnk > 1 

Secondly regarding the question in the title to commit inside a cursor loop is the same as anywhere else. You just need a COMMIT statement. However if you aren't running this inside a larger transaction the UPDATE statement will be auto committed anyway.

Thirdly Your real question doesn't seem to be about commit anyway. It is about the cursor reflecting updates to the data on subsequent iterations. For the case in the question you would need a DYNAMIC cursor

Defines a cursor that reflects all data changes made to the rows in its result set as you scroll around the cursor. The data values, order, and membership of the rows can change on each fetch.

Not all queries support dynamic cursors. The code in the question would but without an ORDER BY it is undeterministic what order the rows would be processed in and thus whether you would see visible results. I have added an ORDER BY and an index to support this to allow a dynamic cursor to be used.

If you try the following you will see the cursor only fetches one row as the dates are processed in descending order and when the first row is processed the table is updated such that no more rows qualify for the next fetch. If you comment out the UPDATE inside the cursor loop all three rows are fetched.

CREATE TABLE MyTable
  (
     [Product]  INT,
     [Customer] INT,
     [Date]     DATETIME,
     [Event]    VARCHAR(10) NULL,
     PRIMARY KEY ([Date], [Product], [Customer])
  )


INSERT INTO MyTable 
VALUES (1,1,'20081201',NULL), 
       (1,1,'20081202',NULL), 
       (1,1,'20081203',NULL)

DECLARE @Product  INT,
        @Customer INT,
        @Date     DATETIME,
        @Event    VARCHAR(10)

DECLARE cur CURSOR DYNAMIC TYPE_WARNING FOR
  SELECT [Product],
         [Customer],
         [Date],
         [Event]
  FROM   MyTable
  WHERE  [Event] IS NULL
  ORDER  BY [Date] DESC

OPEN cur

FETCH NEXT FROM cur INTO @Product, @Customer, @Date, @Event

WHILE @@FETCH_STATUS = 0
  BEGIN
      SELECT @Product,
             @Customer,
             @Date,
             @Event

      -- Now I update my Event value to 'No Event' for records whose date is less than @Date
      UPDATE MyTable
      SET    [Event] = 'No Event'
      WHERE  [Product] = @Product
             AND [Customer] = @Customer
             AND [Date] < @Date

      FETCH NEXT FROM cur INTO @Product, @Customer, @Date, @Event
  END

CLOSE cur

DEALLOCATE cur

DROP TABLE MyTable 


Even if this worked, this would not guarantee the correct result since you miss an ORDER BY clause in your query.

Depending on this, all records, no records or any random subset of records could be updated.

Could you please explain in plain English what your stored procedure should do?


Use Below template

DECLARE @CCount int = 100
    DECLARE @Count int = 0
    DECLARE  @id AS BigInt
    
        
    DECLARE cur Cursor fast_forward for 
        SELECT t1.Id  
             FROM Table1 t1 WITH (NOLOCK) WHERE  <Some where clause>
    OPEN cur
    Begin Tran
    
    While (1=1)
    Begin   
        Fetch next from cur into @id
        If @@Fetch_Status <> 0
            break
    -- do some DML actions
        Delete From Table1 WITH (ROWLOCK) where Id = @id
        Set @count = @count + @@Rowcount
    
        if (@count % @CCount = 0)
        Begin   
            if (@count % 100 = 0)
                Print 'Table1: DML action ' + Cast(@count as Varchar(15)) + ' rows'
    -- for every 100 rows commit tran will trigger , and starts a new one.
            While @@Trancount > 0 Commit Tran
            Begin Tran
        End
    End 
    
    While @@Trancount > 0 Commit Tran
    Close cur
    Deallocate cur
0

精彩评论

暂无评论...
验证码 换一张
取 消