SQL DB脚本性能调优

时间:2014-09-25 14:03:22

标签: sql sql-server

我需要修复一个prod数据库问题和清理脚本我已经花了很长时间。我尝试了几件没有运气的事情,以下是剧本:

DECLARE @ErrorMessage NVARCHAR(4000)
DECLARE @ErrorSeverity INT
DECLARE @ErrorState INT
DECLARE @ErrorProcedure NVARCHAR(50)

BEGIN TRY

    IF OBJECT_ID('tempdb..#SuspectData') IS NOT NULL
    BEGIN
        DROP TABLE #SuspectData 
    END

    CREATE TABLE #SuspectData
      (
          IID INT,
          CID INT,
          PID INT
      )

    INSERT INTO dbo.#SuspectData 
    SELECT DL.IID,DL.CID,IT.PID FROM DL
    INNER JOIN IT ON IT.CID = DL.CID AND IT.IID = DL.IID
    WHERE DL.Suspect = 1


    WHILE (1 = 1)
    BEGIN
        BEGIN TRANSACTION

        UPDATE TOP (5000) TDS
        SET TDS.DTID = 4 
        FROM 
        TDS
        INNER JOIN dbo.#SuspectData SD
        ON TDS.IID = SD.IID AND TDS.PID = SD.PID
        WHERE TDS.DTID <> 4

       IF @@ROWCOUNT = 0
       BEGIN
          COMMIT TRANSACTION
          BREAK
       END

       COMMIT TRANSACTION
    END

    WHILE (1 = 1)
    BEGIN
        BEGIN TRANSACTION

        UPDATE TOP (5000) TDA
        SET TDA.DTID = 4 
        FROM 
        TDA
        INNER JOIN dbo.#SuspectData SD
        ON TDA.IID = SD.IID AND TDA.PID = SD.PID
        WHERE TDA.DTID <> 4

        IF @@ROWCOUNT = 0
        BEGIN
          COMMIT TRANSACTION
          BREAK
        END

       COMMIT TRANSACTION
    END

    DROP TABLE #SuspectData

END TRY

BEGIN CATCH

    SELECT  @ErrorMessage = ERROR_MESSAGE(),
            @ErrorSeverity = ERROR_SEVERITY(),
            @ErrorState = ERROR_STATE(),
            @ErrorProcedure = ERROR_PROCEDURE()

    RAISERROR (@ErrorMessage,@ErrorSeverity,@ErrorState,@ErrorProcedure) ;

END CATCH

我也有以下脚本同时更新所有内容,但它也花了很长时间,比如24小时或其他什么。

DECLARE @ErrorMessage NVARCHAR(4000)
DECLARE @ErrorSeverity INT
DECLARE @ErrorState INT
DECLARE @ErrorProcedure NVARCHAR(50)

BEGIN TRY

    IF OBJECT_ID('tempdb..#SuspectData') IS NOT NULL
    BEGIN
        DROP TABLE #SuspectData 
    END

    CREATE TABLE #SuspectData
      (
          IID INT,
          CID INT,
          PID INT
      )

    INSERT INTO dbo.#SuspectData 
    SELECT DL.IID,DL.CID,IT.PID FROM DL
    INNER JOIN IT ON IT.CID = DL.CID AND IT.IID = DL.IID
    WHERE DL.Suspect = 1

    BEGIN TRANSACTION

        --Update about 1.5M records
        UPDATE TDS
        SET TDS.DTID = 4 
        FROM 
        TDS
        INNER JOIN dbo.#SuspectData SD
        ON TDS.IID = SD.IID AND TDS.PID = SD.PID
        WHERE TDS.DTID <> 4

    COMMIT TRANSACTION

    BEGIN TRANSACTION

        --Update about 4.5M records
        UPDATE TDA
        SET TDA.DTID = 4 
        FROM 
        TDA
        INNER JOIN dbo.#SuspectData SD
        ON TDA.IID = SD.IID AND TDA.PID = SD.PID
        WHERE TDA.DTID <> 4

    COMMIT TRANSACTION

    DROP TABLE #SuspectData

END TRY

BEGIN CATCH

    SELECT  @ErrorMessage = ERROR_MESSAGE(),
            @ErrorSeverity = ERROR_SEVERITY(),
            @ErrorState = ERROR_STATE(),
            @ErrorProcedure = ERROR_PROCEDURE()

    RAISERROR (@ErrorMessage,@ErrorSeverity,@ErrorState,@ErrorProcedure) ;

END CATCH

2 个答案:

答案 0 :(得分:1)

我猜测TDS表很大。在这种情况下,您可以通过在临时表上创建索引来加速临时表和TDS之间的联接操作(ON TDS.IID = SD.IID和TDS.PID = SD.PID):

主要群集:

CREATE TABLE #SuspectData
      (
          IID INT,
          CID INT,
          PID INT,
          CONSTRAINT pk_temp PRIMARY KEY(IID, PID)
      )

或非群集(如果IID-PID对不唯一):

CREATE INDEX IDX_Temp_SuspectData ON #SuspectData(IID,PID)

您还可以检查这些查询的执行计划 - 它将帮助您找到需要这么长时间的操作。 另一方面:如果你能避免使用游标,我通常会反对使用游标。

答案 1 :(得分:0)

首先,是否有任何内容将DL.Suspect = 1更改为其他内容?或者你的数据集是否会继续变大?

我也同意Sean Lange,更新必须是全部还是全部?

我建议使用cursor。游标是分解大型事务以加快使用和减少表锁的好方法。

DECLARE db_cursor CURSOR FOR SELECT DL.IID,DL.CID,IT.PID FROM DL
                            INNER JOIN IT ON IT.CID = DL.CID AND IT.IID = DL.IID
                            WHERE DL.Suspect = 1; 
DECLARE @first INT;
DECLARE @second INT;
DECLARE @third INT;
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO @first , @second , @third ;
WHILE @@FETCH_STATUS = 0  
BEGIN  
      -- Do your updates one row at a time here
      UPDATE TDS
    SET TDS.DTID = 4 
    FROM TDS
    WHERE TDS.IID = @first AND TDS.PID = @third
    WHERE TDS.DTID <> 4
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;