Long running update - looking to help speed it up

  • First off, I didn't write this but did try to make it faster...I've tried looping through it snagging say a TOP 500 records and updating those, then fetching another 500 and so on but it really had no impact.

    I'd think that updating a table with only a few columns, having less than 3,000,000 rows should only take a minute or two?

    Any suggestions on how to make this faster would be greating appreciated!

    /* Snippet */

    DECLARE @mindate datetime SET @mindate = '2010-01-01 00:00:00.000'

    DECLARE @maxdate datetime SET @maxdate = '2010-01-31 23:59:59.000'

    CREATE TABLE #Update (id int identity,

    tranid [varchar](20) NULL,

    fleetnumber [varchar](20) NULL,

    odometer [int] DEFAULT(0),

    pan [varchar](32) NULL,

    unit [varchar](40) NULL,

    trantime [datetime] NULL,

    prevodometer int NULL,

    )

    INSERT INTO #Update

    SELECT DISTINCT

    tranid, fleetnumber, odometer, pan, unit, trantime, prevodometer

    FROM DW_LOAD.[dbo].[Mileage_Table] --> This table has about 3,000,000 rows

    WHERE Odometer > 0

    AND (prevodometer = 0 OR prevodometer IS NULL)

    AND trantime BETWEEN @mindate AND @maxdate

    -- This inserts about 175,000 rows into #Update

    BEGIN TRANSACTION

    UPDATE a

    SET a.prevodometer =

    CASE

    WHEN (dbo.fx_GetLastOdometer(b.trantime,b.pan,b.unit,b.tranid,b.odometer) < 1) THEN 1

    ELSE ISNULL(dbo.fx_GetLastOdometer(b.trantime,b.pan,b.unit,b.tranid,b.odometer), 1)

    END

    FROM dbo.Mileage_Table a

    JOIN #Update b ON

    a.tranid = b.tranid

    AND a.pan = b.pan

    WHERE

    a.trantime BETWEEN @minDate AND @maxDate

    AND (a.prevodometer IS NULL OR a.prevodometer = 0)

    IF @@ERROR = 0

    BEGIN

    COMMIT TRANSACTION

    END

    ELSE

    BEGIN

    /* Send Email */

    EXECUTE msdb.dbo.sp_send_dbmail @recipients = 'me@hotmail.com'

    ,@subject='There was an error - transaction failed'

    ,@body = ''

    ROLLBACK TRANSACTION

    END

    DROP TABLE #Update

    ______________________________________________________________________________Never argue with an idiot; Theyll drag you down to their level and beat you with experience

  • Could you post some sample data, about 10 to 20 rows, as a series of INSERT INTO statements for the source table (and if you haven't provided the CREATE TABLE statement for the source table, please provide that as well). Also, then provide the expected results based on the sample data.

    I have an idea as to what you are trying to accomplish, but it will help to actually see it.

  • It will also help to see the code and DDL for the tables referenced in the fx_GetLastOdometer function.

    (I think that this might be your culprit...)

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • WayneS (1/26/2011)


    It will also help to see the code and DDL for the tables referenced in the fx_GetLastOdometer function.

    (I think that this might be your culprit...)

    Good call on the extra code. I guess I missed it in the jumble of code.

  • Sorry! Yes I guess that would help 🙂

    CREATE FUNCTION [dbo].[fx_GetLastOdometer]

    (@date varchar(40)

    ,@pan VARCHAR(32)

    ,@tranid int)

    RETURNS int

    AS

    BEGIN

    DECLARE @Odometer int

    SET @Odometer = (

    SELECT TOP 1 odometer

    FROM dbo.[Mileage_Table] WITH(INDEX(idx_TranTime))

    WHERE tranid <> @tranid

    AND pan=@pan

    AND trantime < @date

    )

    RETURN @Odometer

    END

    Here's some sample data:

    CREATE TABLE #Mileage_Table (id int identity,

    tranid [varchar](20) NULL,

    fleetnumber [varchar](20) NULL,

    odometer [int] DEFAULT(0),

    pan [varchar](32) NULL,

    unit [varchar](40) NULL,

    trantime [datetime] NULL,

    prevodometer int NULL,

    )

    INSERT INTO #Mileage_Table VALUES

    ('101104','433172', '200369', '5014861143317200022', '3273', '2010-07-08 15:10:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101124', '430943', '11164', '5014861143094300045', '8608', '2010-07-08 15:10:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101128', '433462', '305', '5014861143346200043', '0305', '2010-07-08 15:10:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101135', '434867', '126452', '5014861143486700018', '9685', '2010-07-08 15:10:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101157', '430034', '501618', '5014861143003400027', '4321', '2010-07-08 15:11:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101162', '431788', '11111', '5014861143178800027', '1010', '2010-07-08 15:11:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101169', '430943', '11179', '5014861143094300045', '8608', '2010-07-08 15:11:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101171', '431266', '38544', '5014861143126600016', '2549', '2010-07-08 15:11:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101172', '434002', '101405', '5014861143400200012', '7081', '2010-07-08 15:11:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101182', '436143', '99000', '5014861143614300046', '9570', '2010-07-08 15:11:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101208', '430378', '260590', '5014861143037800036', '0003', '2010-07-08 15:12:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101287', '433659', '10560', '5014861143365900010', '3185', '2010-07-08 15:14:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101371', '437416', '405', '5014861143741600219', '0816', '2010-07-08 15:15:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101377', '435158', '135101', '5014861143515800136', '0013', '2010-07-08 15:15:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101387', '430361', '59092', '5014861143036100024', '4358', '2010-07-08 15:16:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101392', '430791', '8000', '5014861143079100097', '7792', '2010-07-08 15:16:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101436', '433728', '231610', '5014861143372800435', '1950', '2010-07-08 15:17:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101457', '434923', '512806', '5014861143492300035', '6608', '2010-07-08 15:17:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101466', '432431', '75000', '5014861143243100049', '0004', '2010-07-08 15:17:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101467', '433566', '67900', '5014861143356600025', '8039', '2010-07-08 15:17:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101511', '431687', '86532', '5014861143168700013', '0349', '2010-07-08 15:19:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101523', '436602', '92831', '5014861143660200116', '9441', '2010-07-08 15:19:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101532', '430216', '43711', '5014861143021600012', '3000', '2010-07-08 15:19:59.000', NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101537', '433508', '200173', '5014861143350800035', '4321', '2010-07-08 15:19:59.000',NULL)

    INSERT INTO #Mileage_Table VALUES

    ('101551', '430885', '1289', '5014861143088500030', '1289', '2010-07-08 15:20:59.000', NULL)

    -- DROP TABLE #Mileage_Table

    ______________________________________________________________________________Never argue with an idiot; Theyll drag you down to their level and beat you with experience

  • Still missing the expected results based on the sample data. If everything runs correctly, what should the data look like?

  • Lynn, sorry I missed that request. The expected result would be an integer value in the prevodometer column for each record in the table.

    I don't fully understand why they wrote it this way but my understanding is that there are about 1 yearr's worth of odometer readings in that main table. He pulls in a month's worth into the temp table and then updates the main table with the previous odometer's reading for each date/record present in the temp table.

    When it's normally run, there are about 160,000 records in the temp table (which I know is a lot for a temp table).

    Thanks so much for your patience.

    ______________________________________________________________________________Never argue with an idiot; Theyll drag you down to their level and beat you with experience

  • You need to determine what the results should be and provide that. This is used to test our results as well as to understand the process that needs to be completed.

  • Serge,

    Just a quick note on your scalar function - fx_GetLastOdometer. This type of scalar function in a query causes hidden RBAR (Row By Agonizing Row) type processing. The function is doing a select on a table to return a value. This select is done for each row the query. The optimizer doesn't know the table or how to access it when the main query is optimized and so you get a black hole in the query plan - you can't even see that something is non-optimum. I recall that Gail Shaw has an article or two on this in this forum.

    The type of data you want from the function can easily be done with a CROSS APPLY or OUTER APPLY (if there are no records matching) in-line with your query. Paul White has some excellent articles on APPLY in this forum. Using an APPLY will allow the Query Optimizer to figure out the best way to access the table to get the column value you want. This is especially effective if you have a covering index on the ORDER BY column of the TOP 1 (in this case the odometer reading) since you get a straight index seek with no key lookup.

    Todd Fifield

Viewing 9 posts - 1 through 8 (of 8 total)

You must be logged in to reply to this topic. Login to reply