Using a Cursor in a Stored Procudure

时间:2019-05-31 11:25:15

标签: stored-procedures sql-server-2016

I have the following code extremely slow code in a MS SQL 2016 Stored Procedure (I think it is stuck in a never ending loop):

@DBID Integer
AS
BEGIN

DECLARE TagCursor CURSOR FOR SELECT MemberID
FROM           ADMIN_API_Master_Members_List
WHERE DBID= @DBID AND Len(Pending) > 0 

DECLARE @ProgramCode VARCHAR(10)
DECLARE @Values VARCHAR(MAX)
DECLARE @tag nvarchar(10) 

  OPEN TagCursor

      FETCH NEXT FROM TagCursor INTO @tag
      WHILE (@@FETCH_STATUS = 0)
      BEGIN

     SELECT @ProgramCode = Program_Code, @Values= Pending FROM  ADMIN_API_Master_Members_List WHERE MemberID= @tag 

     DELETE FROM ADMIN_API_PMID_PROGRAM_CODE_HOLDING_TABLE
     WHERE  (MemberID =@tag)

     INSERT INTO ADMIN_API_PMID_PROGRAM_CODE_HOLDING_TABLE ( ProgramCode, MemberID, DBID, PMID) 
     SELECT    @ProgramCode, @tag, @DBID ,  Value FROM STRING_SPLIT(@Values, ',')       
     END

      CLOSE TagCursor
      DEALLOCATE TagCursor 
END

This Procedure is only a maintenance process and does not get run very often but when it does it would be nice to only take a few seconds. The purpose is to normalize in a Table one record for each comma seperated value in the ADMIN_API_Master_Members_List Table and put it into the ADMIN_API_PMID_PROGRAM_CODE_HOLDING_TABLE with the Program_Code and MemberID and DBID.

There are only about 150 records in the Master Table and the comma separated strings may have 5 values. I am receptive to other solutions.

Thanks in advance

2 个答案:

答案 0 :(得分:1)

The reason for the infinite loop may be that you have no "Fetch next" inside your loop Try the below:

  @DBID Integer
  AS
  BEGIN

  DECLARE TagCursor CURSOR FOR SELECT MemberID
  FROM           ADMIN_API_Master_Members_List
  WHERE DBID= @DBID AND Len(Pending) > 0 

  DECLARE @ProgramCode VARCHAR(10)
  DECLARE @Values VARCHAR(MAX)
  DECLARE @tag nvarchar(10) 

    OPEN TagCursor
        FETCH NEXT FROM TagCursor INTO @tag   

        WHILE (@@FETCH_STATUS = 0)
        BEGIN

       SELECT @ProgramCode = Program_Code, @Values= Pending FROM  ADMIN_API_Master_Members_List WHERE MemberID= @tag 

       DELETE FROM ADMIN_API_PMID_PROGRAM_CODE_HOLDING_TABLE
       WHERE  (MemberID =@tag)

       INSERT INTO ADMIN_API_PMID_PROGRAM_CODE_HOLDING_TABLE ( ProgramCode, MemberID, DBID, PMID) 
       SELECT    @ProgramCode, @tag, @DBID ,  Value FROM STRING_SPLIT(@Values, ',')  
      FETCH NEXT FROM TagCursor INTO @tag    
       END

        CLOSE TagCursor
        DEALLOCATE TagCursor 
  END

答案 1 :(得分:1)

I haven't tested this, but like I mentioned in the comments, using a CURSOR is a bad idea; they are inherently slow as SQL Server excels at set based methods not iterative tasks (and a CURSOR is the latter).

I suspect that this achieves the answer you're after and avoids the CURSOR all together:

CREATE PROC YourProc @DBID integer
AS
BEGIN

    DECLARE @Deleted table (ProgramCode varchar(10),
                            [Value] varchar(MAX),
                            Tag nvarchar(10));


    DELETE HT
    OUTPUT deleted.Program_Code,
           deleted.Pending,
           deleted.MemberID
    INTO @Deleted (ProgramCode,
                   [Value],
                   Tag)
    FROM ADMIN_API_PMID_PROGRAM_CODE_HOLDING_TABLE AS HT
         JOIN ADMIN_API_Master_Members_List AS MML ON HT.MemberID = MML.MemberID
    WHERE MML.[DBID] = @DBID
      --AND LEN(Pending) > 0; --Changed this to below to be SARGable, as only a string with the value '' will have a length of 0.
      AND Pending != '';

    INSERT INTO ADMIN_API_PMID_PROGRAM_CODE_HOLDING_TABLE (ProgramCode,
                                                           MemberID,
                                                           DBID,
                                                           PMID)
    SELECT D.ProgramCode,
           D.Tag,
           @DBID,
           SS.Value
    FROM @Deleted AS D
         CROSS APPLY STRING_SPLIT(D.[Value], ',') AS SS;

END;