무한루프/코딩, 개발

MS-SQL에서 CURSOR 사용: 성능과 대안

시원한생맥주 2025. 3. 5. 12:34

MS-SQL CURSOR 성능과 대안
MS-SQL CURSOR 성능과 대안

 

CURSOR란 무엇인가?

MS-SQL에서 CURSOR는 행(Row) 단위로 데이터를 처리할 때 사용되는 객체이다. 일반적으로 SELECT 문을 사용하면 결과 집합을 한꺼번에 가져오지만, CURSOR를 사용하면 한 행씩 순차적으로 데이터를 가져와 처리할 수 있다.

 

CURSOR의 기본 사용 예제

다음은 CURSOR를 사용하여 TB_0_Member 테이블에서 데이터를 조회하고, 이를 SP_Member_INSERT 프로시저를 호출하는 방식이다.

DECLARE @MemberGroup VARCHAR(200), @MemberName VARCHAR(200), @Mobile VARCHAR(200)
DECLARE TempCursor CURSOR  -- 커서 선언
 FOR
    SELECT MemberGroup, MemberName, Mobile FROM TB_0_Member

OPEN TempCursor -- 커서 열기
FETCH NEXT FROM TempCursor INTO @MemberGroup, @MemberName, @Mobile -- 첫 번째 행 가져오기

WHILE @@FETCH_STATUS = 0 -- 데이터가 존재하는 동안 반복
BEGIN
    EXEC SP_Member_INSERT 0, @MemberName, @Mobile, @MemberGroup  -- 저장 프로시저 실행
    FETCH NEXT FROM TempCursor INTO @MemberGroup, @MemberName, @Mobile -- 다음 행 가져오기
END

CLOSE TempCursor  -- 커서 닫기
DEALLOCATE TempCursor  -- 커서 해제

 

CURSOR의 문제점

CURSOR는 편리하지만, 다음과 같은 단점이 있다:

  1. 성능 저하: 한 행씩 처리하기 때문에 대량 데이터를 다룰 때 성능이 매우 저하될 수 있다.
  2. 메모리 사용 증가: 서버 메모리를 많이 사용하여 리소스 낭비가 발생할 수 있다.
  3. 병렬 처리 불가능: 한 번에 한 행만 처리하므로 대량 작업 시 병렬 처리가 어렵다.

 

CURSOR의 대안

1. SET-Based 연산

대부분의 경우, CURSOR 없이 SET-Based 방식으로 데이터를 한꺼번에 처리하는 것이 성능 면에서 훨씬 효율적이다. 예를 들어, 위의 CURSOR 예제는 다음과 같은 INSERT 문으로 대체할 수 있다:

INSERT INTO TB_1_Member (MemberGroup, MemberName, Mobile)
SELECT MemberGroup, MemberName, Mobile FROM TB_0_Member;

이 방식은 대량 데이터를 처리하는 데 훨씬 더 빠르고 효율적이다.

2. WHILE 루프 사용

만약 특정 로직이 필요한 경우, WHILE 문과 IDENTITY 컬럼을 활용할 수도 있다.

DECLARE @ID INT = 0
DECLARE @MAX_ID INT
SELECT @MAX_ID = MAX(ID) FROM TB_0_Member

WHILE @ID <= @MAX_ID
BEGIN
    EXEC SP_Member_INSERT 0, (SELECT MemberName FROM TB_0_Member WHERE ID = @ID),
                             (SELECT Mobile FROM TB_0_Member WHERE ID = @ID),
                             (SELECT MemberGroup FROM TB_0_Member WHERE ID = @ID)
    SET @ID = @ID + 1
END

결론

CURSOR는 특정 상황에서는 유용하지만, 성능 저하 문제로 인해 대량 데이터 처리에는 권장되지 않는다. SET-Based 연산이나 WHILE 루프를 적절히 활용하면 성능을 향상시킬 수 있다. 가능하면 CURSOR 사용을 피하고, 효율적인 SQL 쿼리 작성을 고려하는 것이 좋다.

 

 

*이대론 아쉬우니 추가 예제 투척

DECLARE @CCU_CHANGETEXT5 VARCHAR(20), @CONTROL_NO VARCHAR(4000)

SET @CONTROL_NO = ' AND CONTROL_NO IN ('

DECLARE TEMPCURSOR CURSOR
 FOR

  SELECT 
   CUS1.CCU_CHANGETEXT5
  FROM  
   ITSM_CHANGES     CHA1

   --변경등급
   INNER JOIN ITSM_CHA_CUSTOM_FIELDS CUS1
    ON CHA1.CHA_OID = CUS1.CCU_CHA_OID
   
   --상태값 가져오기
   INNER JOIN REP_CODES_TEXT  RCT1
    ON CHA1.CHA_STA_OID = RCT1.RCT_RCD_OID AND RCT1.RCT_LNG_OID=1042

  WHERE 1=1
   AND RCT_NAME <> '변경 대기'  AND CCU_CHANGEDATE8 >= '2013-02-01 00:00:00.000'  AND CCU_CHANGEDATE8 <= '2013-02-28 23:59:59.000'

OPEN TEMPCURSOR
 FETCH NEXT FROM TEMPCURSOR INTO @CCU_CHANGETEXT5
 WHILE @@FETCH_STATUS = 0
 BEGIN
  SET @CONTROL_NO = @CONTROL_NO + @CCU_CHANGETEXT5 + ','
  FETCH NEXT FROM TEMPCURSOR INTO @CCU_CHANGETEXT5
 END
CLOSE TEMPCURSOR
DEALLOCATE TEMPCURSOR


PRINT LEFT(@CONTROL_NO,LEN(@CONTROL_NO)-1) + ')'