Take the 2-minute tour ×
Database Administrators Stack Exchange is a question and answer site for database professionals who wish to improve their database skills and learn from others in the community. It's 100% free, no registration required.

I need to update 100 million records in a single table, in effect, normalizing the table by replacing the varchar value of a column with simply an ID. (I say "replacing" but really I'm writing the ID into another column.)

What I'm trying to achieve is to normalize the dataset. The not yet normalized data has no indexing. My thought was that I would not build indexes on the raw values, waiting, instead to index the foreign keys that will be replacing the varchar values with tinyint values after the update completes.

UPDATE A
SET A.AutoClassID = B.AutoClassID
FROM AutoDataImportStaging.dbo.Automobile as A
JOIN AutoData.dbo.AutoClass as B on (A.AutoClassName = B.AutoClassName)

Background

  • using MSSQL 2008 R2 on Server 2008 R2
  • server has 8 GB RAM
  • server has one RAID10, 7200 RPM SATA (not great, I know, in production this will only read data and not write data; plus recent HD shortage made this necessary for cost)
  • server has dual quad-core Xeon CPU
  • the machine is not doing anything else (currently dedicated to dev, only this process)
  • simple logging turned on (? - but does it still log so that it can rollback?)
  • note that the query references two different DBs, for what that's worth
  • "width" of a record in table getting updated is 455 bytes

Resources During Execution

  • physical RAM is maxed out
  • disk I/O is maxed out
  • CPU is hardly doing anything (choke point is I/O)
  • run time has been 14 hours and counting!

I suspect a few things like I need an index on the raw data, even though I will be dropping the column (AutoClassName) after the normalization updates. I also wonder if I should just loop down the table one record at a time instead of the JOIN, which seemed ridiculous at the time I started this, but now it seems that would have been faster.

How should I change my methodology for my remaining normalization updates (similar to this one) more quickly?

share|improve this question
add comment

5 Answers

up vote 5 down vote accepted

You're trying to do this as a single (very large) transaction. Instead, do the update in smaller batches.

You would also benefit from:

  • A temporary index on AutoData.dbo.AutoClass.AutoClassName
  • More RAM. Lot's more RAM.
share|improve this answer
1  
+1 I agree with the batch updating using the TOP clause. That'd be my approach. –  Thomas Stringer Jul 6 '12 at 15:10
    
If I do UPDATE TOP then I'll need a WHERE clause (WHERE AutoClassID is NULL)? Wouldn't the WHERE clause introduce a new performance hit (a table scan I'm not doing now). No doubt it would diminish the RAM problem I'm having with the JOIN. –  Chris Adragna Jul 6 '12 at 15:20
    
My response is long overdue, but in my case, SET ROWCOUNT proved to be the most effective. –  Chris Adragna Sep 6 '13 at 21:44
add comment

I would take a different approach.

Instead of updating existing tables, just build a new table that has what you need in it.

This will almost certainly be faster:

SELECT DISTINCT
    AutoClassID,
    <Other fields>
INTO
    AutoDataImportStaging.dbo.Automobile
FROM
    AutoData.dbo.AutoClass

As currently written, there are a lot of logical operations happening:

  • Read all values of A.AutoClassName
  • Read all values of B.AutoClassName
  • Compare A and B values
  • Of the matching set, read all values of B.AutoClassID
  • Update existing values of A.AutoClassId to be the B.AutoClassId value through whatever indexes exist
share|improve this answer
    
This sounds like a nice, simple approach, especially given the disk I/O problem I'm having. Thank you for answering so quickly. –  Chris Adragna Jul 6 '12 at 15:16
1  
I suggest that you double-check that you have enough free space in your log and data files. If the files are auto-growing, performance will nose-dive. I often see people running some big, one-off update and auto-growing their log file without realizing it. –  darin strait Jul 6 '12 at 18:12
add comment

Looping down the table one row at a time, will not be faster!

As suspected, and confirmed by you, this will be i/o bound - having one disk, the reads, write, transaction logs and (any) temp work space will all be competing for the same i/o.

Simple recovery will still log the transactions, but the log will be cleared by a checkpoint. It's possible that you initial log size and auto-growth settings are causing some i/o slow down - the transaction log will need to grow to accommodate the changes.

Have you tried indexing the AutoClassName field? How many different AutoClass values are there?

You may need to batch the updates, based on limitations of your i/o. So update 1 million, checkpoint, repeat....

share|improve this answer
    
There are only 15 different AutoClass values. Your comments confirm many of my suspicions (and pains!). Thank you for answering. –  Chris Adragna Jul 6 '12 at 15:12
add comment

Create indexes for the joining fields.

You can always drop the indexes when you are finished.

I would be very surprised if the indexes did not significantly improve the update performance.

share|improve this answer
    
I'm sure indexes would improve. I suppose the question is whether they improve more than the time it takes to create the index (for one use only). Probably yes. :) –  Chris Adragna Jul 6 '12 at 21:24
add comment

Export the way you want, create a new table and import back. As a Bonus, you would have a copy of the data as backup, should miracles happen.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.