Optimize Stored Procedure

  • Hello,
    I have this stored proc I am trying to determine if it can be optimized for better performance. Currently it takes 15 minutes to INSERT 28,000 records into [Email] table on a VM with multiple CPU and 16GB RAM on SQL Server 2014. If anyone could advise it would be appreciated.


    USE [cool_mail]
    GO
    /****** Object: StoredProcedure [dbo].[procInsertBatchEmailList]  Script Date: 9/26/2018 8:55:16 AM ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    ALTER PROCEDURE [dbo].[procInsertBatchEmailList]
    -- Params
    @batchID int,
    @responseCode int OUTPUT
    AS
    BEGIN
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
        DECLARE @rowcount int,
            @agentID int,
            @agentInfoID int,
            @emailStatusID int,
            @totalRowCount int,
            @sendEmailToCustomers bit,
            @error int,
            @isAgentType bit,
            @repID int

    BEGIN TRY

    BEGIN TRANSACTION tranInsertBatchEmailList

    IF OBJECT_ID('#TempAgentRep') IS NOT NULL
      DROP TABLE #TempAgentRep

    CREATE TABLE #TempAgentRep(                        
            guid uniqueidentifier NOT NULL,
            repID int NOT NULL,
            firstName varchar(50) NOT NULL,
            lastName varchar(50) NOT NULL,
            phone varchar(20) NULL,
            emailAddress varchar(100) NOT NULL,
            hasPhoto bit NOT NULL,
            keycode varchar(30) NULL)

    IF OBJECT_ID('#TempEmail') IS NOT NULL
                DROP TABLE #TempEmail

    CREATE TABLE #TempEmail(                        
            securityCode uniqueidentifier NOT NULL,
            batchId int NOT NULL,
            emailStatusId int NOT NULL,
            clientName varchar(255) NOT NULL,
            clientEmail varchar(255) NULL,
            keycode varchar(30) NULL,
            agentID int NULL)

    SET @totalRowCount = 0

    SELECT @emailStatusID = emailStatusID
    FROM EmailStatus WHERE description = 'Initialized' -- initial email status

    SELECT @isAgentType = isAgentType FROM BatchType
    INNER JOIN Batch ON Batch.batchTypeID = BatchType.batchTypeiD
    WHERE batchID = @batchID

    -- if batch is of agent type
    IF @isAgentType = 1
    BEGIN
        -- Insert agent data into email
        PRINT 'Insert agent data into email'
        INSERT INTO Email (securityCode, batchID, emailStatusID, clientName, clientEmail)
        SELECT NEWID(), @batchID, @emailStatusID, AgentName, AgentEMail
            FROM cqo1.dbo.AgentInfo
            LEFT JOIN cqo1.dbo.AgentMessage On AgentInfo.ID = AgentMessage.AgentInfoID
            AND AgentMessage.isActive = 1
            WHERE AgentInfo.ID IN (SELECT agentInfoID FROM BatchAgentInfo WHERE batchID = @batchID)
    END
    ELSE
    BEGIN
        DECLARE agent_cursor CURSOR FOR
            SELECT agentInfoID FROM BatchAgentInfo WHERE batchID = @batchID

        OPEN agent_cursor

        --PreFetch
        FETCH NEXT FROM agent_cursor
        INTO @agentInfoID

        --Start processing
        PRINT 'Start processing...'
        WHILE @@FETCH_STATUS = 0
        BEGIN
            INSERT INTO Agent (agentInfoID, name, emailAddress, phone, logofile, hasBinaryLogo, message, QRCode, QRCodeUrl, isActive)
            SELECT AgentInfo.ID, AgentName, AgentEMail, ContactPhone, LogoName, CASE WHEN AgentInfo.logoImage IS NULL THEN 0 ELSE 1 END, AgentMessage.description, qrCodeDesc, qrCodeUrl, 1
            FROM cqo1.dbo.AgentInfo
            LEFT JOIN cqo1.dbo.AgentMessage On AgentInfo.ID = AgentMessage.AgentInfoID
            AND AgentMessage.isActive = 1
            WHERE AgentInfo.ID = @agentInfoID

            SELECT @agentID = @@IDENTITY, @error = @@ERROR

            IF @error <> 0 OR @rowCount < 0
            BEGIN
                PRINT 'Insert into Agent failed'
                RAISERROR('Insert into Agent failed', 11, 1)
            END

            
            -- delete all data from temp table
            TRUNCATE TABLE #TempEmail
            PRINT '(TRUNCATE) deleted all data from temp table'
            -- insert racode data into email table
            INSERT INTO #TempEmail (securityCode, batchID, emailStatusID, clientName, clientEmail, keycode, agentID)
            SELECT NEWID(), @batchID, @emailStatusID, RACodes.UserName, RACodes.UserEmail, RACodes.RACode, @agentID
            FROM cqo1.dbo.RACodes
            WHERE ID IN
            (
            SELECT MAX(ID)
            FROM cqo1.dbo.RaCodes
            GROUP BY UserEmail, AgentID
            HAVING UserEmail IN (SELECT UserEmail FROM cqo1.dbo.RaCodes
                    WHERE AgentID = @agentInfoID
                    AND UserEmail IS NOT NULL
                    AND UserEmail <> ''
                    AND emailOptOut = 0
                    AND cancelled = 0))
       AND AgentID = @agentInfoID    
       ORDER BY RACodes.AgentID, RACodes.USerEmail DESC

            PRINT 'inserted racode data into temp table'

            INSERT INTO Email (securityCode, batchID, emailStatusID, clientName, clientEmail, keycode, agentID)
            SELECT securityCode, batchID, emailStatusID, clientName, clientEmail, keycode, agentID FROM #TempEmail
            WHERE keycode NOT IN ( SELECT e.keycode from Email e
                    INNER JOIN Batch b on e.batchID = b.batchID where b.welcomeID IS NOT NULL
                    AND e.emailStatusID = 1 AND b.batchStatusID = 1 AND keyCode is NOT NULL )

            PRINT 'inserted Email data into temp table'
            SELECT @error = @@ERROR, @rowCount = @@ROWCOUNT    

            IF @error <> 0 OR @rowCount < 0
            BEGIN
                PRINT 'Insert into Email failed'
                RAISERROR('Insert into Email failed', 11, 1)
            END
            
    --        SET @totalRowCount = @totalRowCount + @rowCount
            FETCH NEXT FROM agent_cursor
                INTO @agentInfoID
        END

        --Close cursor
        CLOSE agent_cursor
        DEALLOCATE agent_cursor

        -- INSERT AgentRep data
        INSERT INTO #TempAgentRep(guid, repID, firstName, lastName, phone, emailAddress, hasPhoto, keyCode)
        SELECT DISTINCT NEWID(), asi.id, asi.agentFirstName, asi.agentLastName, asi.agentPhone, asi.agentEmail, CASE WHEN asi.photograph IS NULL THEN 0 ELSE 1 END , asic.raCode
        FROM cqo1.dbo.AgentSubInfo asi
        INNER JOIN cqo1.dbo.AgentSubInfoCodes asic
        ON asic.agentSubInfoID = asi.id
        AND asic.AgentSubInfoId <> -1
        WHERE asic.raCode IN (SELECT keyCode FROM dbo.Email WHERE batchID = @batchID)    

        INSERT INTO AgentRep (agentIndentifier, repID, firstName, lastName, phone, emailAddress, hasPhoto)
        SELECT guid, repID, firstName, lastName, phone, emailAddress, hasPhoto FROM #TempAgentRep

        SELECT @error = @@ERROR

        IF @error <> 0
            RAISERROR('Insert into AgentRep failed', 11, 1)

        -- Update Email with agentRepID
            UPDATE Email
        SET agentRepID = ar.agentRepID
        FROM Email
        INNER JOIN #TempAgentRep asic
        ON Email.keyCode = asic.keycode    
        INNER JOIN AgentRep ar
        ON asic.repID = ar.repID
        AND ar.agentIndentifier = asic.guid
        --INNER JOIN #TempAgentRep tar
        --ON ar.agentIndentifier = tar.guid
        --WHERE asic.keycode IN (SELECT keyCode FROM dbo.Email WHERE batchID = @batchID)
        --AND asic.agentSubInfoId <> -1
        WHERE Email.batchID = @batchID

        SELECT @error = @@ERROR

        IF @error <> 0
            BEGIN
            PRINT 'Update of Email failed'
            RAISERROR('Update of Email failed', 11, 1)
            END

    END
    COMMIT TRANSACTION tranInsertBatchEmailList
    SET @responseCode = 1
    RETURN @totalRowCount

    END TRY

    BEGIN CATCH

      DECLARE @errorMessage nvarchar(4000),
        @errorSeverity int,
        @errorState int,
        @errorLine int

        --Close cursor
        CLOSE agent_cursor
        DEALLOCATE agent_cursor

      IF @@TRANCOUNT > 0
       ROLLBACK TRANSACTION tranInsertBatchEmailList

      SELECT
       @errorMessage = ERROR_MESSAGE(),
       @errorSeverity = ERROR_SEVERITY(),
       @errorState = ERROR_STATE(),
       @errorLine = ERROR_LINE();

        PRINT 'Unable to save Batch data for ' + CAST(ISNULL(@batchID, '') AS VARCHAR(10)) +
        ';' + @errorMessage + ' line number: ' + CAST(@errorLine AS VARCHAR(10))

      SELECT @errorMessage = 'Unable to save Batch data for ' + CAST(ISNULL(@batchID, '') AS VARCHAR(10)) +
        ';' + @errorMessage + ' line number: ' + CAST(@errorLine AS VARCHAR(10))

      RAISERROR (@errorMessage, @errorSeverity, @errorState);

      RETURN -1

    END CATCH
    END

  • I would start with the cursor.  Have you narrowed down which part is taking the longest? You can probably remove the cursor and do a set based operation and output certain values into a temp table.

    For better, quicker answers, click on the following...
    http://www.sqlservercentral.com/articles/Best+Practices/61537/

    For better answers on performance questions, click on the following...
    http://www.sqlservercentral.com/articles/SQLServerCentral/66909/

  • All those SELECT statements used to put the data together could potentially be tuned. I'd check each of the execution plans to see what's happening. Also this:
    WHERE asic.raCode IN (SELECT keyCode FROM dbo.Email WHERE batchID = @batchID)
    Might work better as a JOIN than as an IN. You'd need to test it to be sure.

    "The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
    - Theodore Roosevelt

    Author of:
    SQL Server Execution Plans
    SQL Server Query Performance Tuning

Viewing 3 posts - 1 through 2 (of 2 total)

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