views:

49

answers:

3

Hi,

I need to update millions of rows as part of my next release, but doing so fills the transaction log and fails. I have a few ideas but I'm not a SQL expert so I'm sure there will be gotchas that I'm not aware of.

Pertinent points:

  1. I need to hand a script over to the operations team so need a T-SQL method with no manual intervention.
  2. Apparently the transaction log gets recycled every 15 minutes. (I've thought about writing a loop with a try-catch with WAITFOR DELAY '00:15:00' in the catch block like below)
  3. (EDIT) I can't modify anything except the data.
  4. (EDIT) It's a simple update changing a foreign key column to a different existing key.

Thanks,

Phil

DECLARE
    @AffectedRows int

SET @AffectedRows = 0

WHILE @AffectedRows < @RowsToUpdate
BEGIN
    BEGIN TRY
        BEGIN TRAN
        -- Do some updates  
        SET @AffectedRows = @AffectedRows + @@RowCount
        COMMIT TRAN
    END TRY
    BEGIN CATCH
        PRINT ERROR_MESSAGE()
        WAITFOR DELAY '00:15:00'
    END CATCH
END

PRINT @AffectedRows
+1  A: 

A few points / ideas:

  1. You can expand your transaction log to whatever size you want so it does not fill it.
  2. If your transaction log grows too much you can always backup your DB and truncate the log.
  3. You can work through the data in batches (do a million at a time)
  4. You can copy the data to a working table and then sp_rename it in when the processing is done.
Sam Saffron
I forgot to mention that I can't make any kind of schema or admin type changes. I've updated the question to reflect that. Do you have any examples on performing updates in batches?
Phil
@Phil it depends on the schema of the table and if you have some natural way telling that an update happened or partitioning the table, edit the question to include the schema and update statement ...
Sam Saffron
I'm just updating an ID to a different ID, so I'm using `where ID = @OldID`.
Phil
@Phil, the thing is if you have an increasing PK then you can do where PK between 0 +@i and 100000 + @i , note you will probably want to commit the transaction after each batch otherwise your tran log will fill up again and leave you at square 1
Sam Saffron
Basically like the example I have in the question?
Phil
A: 

You're reinventing nibbling deletes/updates :)

Take a look at this approach, you can do bigger blocks than a single row:

http://www.sqlservervideos.com/video/nibbling-deletes/

http://sqladvice.com/blogs/repeatableread/archive/2005/09/20/12795.aspx

Meff
Ok, but I'm not worried about concurrency - I need to be able to handle the case where the queries fill the transaction log.
Phil
+1  A: 

In the end the example I had already written worked best; a transaction log full error gets caught in the catch and 15 minutes is long enough for the log to be recycled.

DECLARE 
    @AffectedRows int 

SET @AffectedRows = 0 

WHILE @AffectedRows < @RowsToUpdate 
BEGIN 
    BEGIN TRY 
        BEGIN TRAN 
        -- Do some updates   
        SET @AffectedRows = @AffectedRows + @@RowCount 
        COMMIT TRAN 
    END TRY 
    BEGIN CATCH 
        PRINT ERROR_MESSAGE() 
        WAITFOR DELAY '00:15:00' 
    END CATCH 
END 

PRINT @AffectedRows
Phil