views:

121

answers:

1

What is the best alternative to using a cursor in SQL if I am suffering from performance issues ?

I got the following code wherein it uses Cursor to loop through and insert records.

--Extract Column Data From Pit To Port AuditLog to QmastorAudit AuditLog

  DECLARE @AuditBatchID_logRow INT,
 @AuditOperationID_logRow INT,
 @RowIdentifier_logRow nvarchar(200),
 @AuditDBTableID_logRow INT, 
 @AuditLogRowID INT,

 @AuditDBColumnID INT, 
 @NewValue nvarchar(200),
 @PreviousVaue nvarchar(200), 
 @NewDisplayValue nvarchar(200)

  DECLARE Crsr_AUDITLOGROW CURSOR LOCAL FORWARD_ONLY STATIC 
      FOR 
         SELECT [t0].[AuditBatchID], 
                [t1].[AuditOperationID], 
                [t1].[RowIdentifier],
                [t0].[AuditTableID],
                [t1].[AuditLogRowID]
         FROM [AuditBatchTable] AS [t0]
         INNER JOIN [AuditLogRow] AS [t1] 
               ON [t0].[AuditBatchTableID] = [t1].[AuditBatchTableID]

  Open Crsr_AUDITLOGROW

  FETCH NEXT FROM Crsr_AUDITLOGROW 
     INTO @AuditBatchID_logRow, 
          @AuditOperationID_logRow,  
          @RowIdentifier_logRow, 
          @AuditDBTableID_logRow,
          @AuditLogRowID

  While(@@FETCH_STATUS = 0)
  BEGIN
      INSERT INTO AuditLog(AuditLogRowID, AuditColumnID, 
                           NewValue, OldDisplayValue, NewDisplayValue)
        (SELECT @AuditLogRowID,
                [ac].[AuditColumnID], 
                [t0].[UserEnteredValue], 
                [t0].[PreviousDisplayValue],
                [t0].[DisplayValue]
          FROM FMG_PROD.dbo.AuditLog AS [t0]
          INNER JOIN FMG_PROD.dbo.AuditDBColumn AS [t1] 
             ON [t0].[AuditDBColumnID] = [t1].[AuditDBColumnID]
          INNER JOIN FMG_PROD.dbo.AuditDBTable AS [t2] 
             ON [t1].[AuditDBTableID] = [t2].[AuditDBTableID]
          INNER JOIN AuditTable AS [AT] 
             ON [t2].AuditDBTable = [AT].AuditTable
          INNER JOIN AuditColumn AS [AC] 
             ON [AT].AuditTableID = [AC].AuditTableID 
          WHERE  
             ([t0].[AuditBatchID] = @AuditBatchID_logRow)  
             AND ([t0].[AuditOperationID] = @AuditOperationID_logRow)
             AND ([AT].[AuditTableID] = @AuditDBTableID_logRow) 
             AND [AC].AuditColumn = [t1].AuditDBColumn 
             AND (@RowIdentifier_logRow = 
                CASE ISNUMERIC(@RowIdentifier_logRow)
                  WHEN 1 then 
                      CAST ([t0].[RowID] AS VARCHAR(200))
                  ELSE 
                      CAST([t0].[RowGUID] AS VARCHAR(200))
  END))

         FETCH NEXT FROM Crsr_AUDITLOGROW 
           INTO @AuditBatchID_logRow, 
                @AuditOperationID_logRow, 
                @RowIdentifier_logRow, 
                @AuditDBTableID_logRow,
                @AuditLogRowID
END

CLOSE Crsr_AUDITLOGROW
DEALLOCATE Crsr_AUDITLOGROW
+3  A: 

Well, you're thinking and coding like a structured programmer - linearly, one by one, in tighest control of the program flow. That's how we (almost) all have been thought to program.

You need to think like a SQL guy - in SETS of data (not single rows, one at a time).

Avoid the need to tightly control each step of the algorithm - instead, just tell SQL Server WHAT you want - not HOW to do each step!

In the end, you're inserting a bunch of rows into the AuditLog table. Why do you need a cursor for that??

 INSERT INTO AuditLog(...list of columns.....)
    SELECT (....list of columns....)
    FROM Table1
    INNER JOIN ..........
    INNER JOIN .........
    WHERE ........

and you're done! Define what you want inserted into the table - DO NOT tell SQL Server in excrutiating detail how to do it - it'll know very well, thank you!

Marc

marc_s
+1 for the importance of thinking in sets.
APC
Thanks Marc it helped me a lot. Reduces lot of code and got better performance.
Nev_Rahd
Great! Glad I could help!
marc_s