본문 바로가기
프로그래밍&IT/MySQL, Oracle, SQL Server

MS Sql Server - cursor

by 성장의 용 2025. 7. 28.
728x90
반응형

커서 (cursor)를 사용하는 이유

SQL은 기본적으로 집합 (set)기반 언어이며 다음 같은 경우 레코드 하나하나를 반복해서 처리할 필요가 있다.

 

  1. 레코드 단위로 복잡한 로직 처리
    • 예: 조건에 따라 다른 테이블에 INSERT/UPDATE
    • 예: 이전 행 값을 참조해서 현재 행 처리
  2. 동적 SQL, 또는 조건 분기 로직 필요
    • SELECT 결과에 따라 프로시저 실행, 로그 기록 등
  3. 다양한 테이블 또는 조건에 따라 서로 다른 작업 수행
  4. 다중 트랜잭션 처리 필요
    • 레코드마다 커밋 또는 롤백이 필요한 경우

 

항목 WHILE + 임시 테이블 CURSOR
처리 단위 집합 기반 or 인덱스 기반 루프 행(row) 기반 반복
코드 복잡도 비교적 단순 선언, OPEN, FETCH, CLOSE 필요
성능 보통 더 빠름 상대적으로 느림
유연성 제한적 매우 유연 (방향 이동, 커서 유형, 동시성 제어 등)
대표 예 단순 루프, 숫자 증가, 인덱스 기반 반복 행마다 조건 분기, 복잡한 로직 필요 시

다음과 같은 단점이 존재한다.

  • 성능 저하 반복 처리이므로 집합 기반보다 훨씬 느림 (특히 대용량 시)
  • 자원 소모 서버 리소스(메모리, CPU)를 더 많이 사용함
  • 복잡한 코드 구조 선언/OPEN/FETCH/조건/종료 코드 등 구조가 길어짐
  • 병목 발생 가능 트랜잭션 안에서 커서를 오래 사용 시 동시성 저하 가능

커서 대신 사용할 수 있는 대안들

  • WHILE + ROW_NUMBER() or IDENTITY 인덱스 기반 반복 처리
  • MERGE, UPDATE ... FROM, CASE 등 조건 분기 포함한 집합 처리
  • 사용자 정의 함수 또는 프로시저 집합 기반 처리로 성능 개선 가능
  • 임시 테이블 or 테이블 변수 + 루프 커서 대신 데이터 저장 후 인덱스 접근

 

1. 커서 선언 기본 형식

DECLARE cursor_name CURSOR 
    [ LOCAL | GLOBAL ]
    [ FORWARD_ONLY | SCROLL ]
    [ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
    [ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
FOR
    SELECT ...

2. 커서 범위 옵션

  • LOCAL- 현재 배치(batch) 또는 프로시저 내에서만 사용 가능 (기본값은 DB 설정에 따라 다름)
  • GLOBAL - 커서가 연결된 세션 전체에서 사용 가능

3. 스크롤 방향 옵션

  • FORWARD_ONLY - 순방향으로만 이동 가능 (가장 빠름, 기본 옵션 중 하나)
  • SCROLL -  NEXT, PRIOR, FIRST, LAST, ABSOLUTE, RELATIVE 등 임의의 방향으로 이동 가능

4. 커서 유형 옵션 (데이터 스냅샷 방식)

  • STATIC - 쿼리 결과를 임시 테이블에 저장. 결과가 고정되어 커서 이동이 빠름. 데이터 변경 반영 안 됨.
  • KEYSET - 결과 집합의 키만 고정. 값은 실시간 반영됨. 삽입된 행은 보이지 않음. 삭제된 행은 감지됨.
  • DYNAMIC - 완전히 동적. 삽입/수정/삭제 모두 실시간 반영됨. 성능은 가장 느릴 수 있음.
  • FAST_FORWARD - FORWARD_ONLY + READ_ONLY 조합으로 가장 빠름. 데이터 수정 불가, 커서 이동은 순방향만 가능.

5. 동시성 옵션 (잠금/변경 감지 방식)

  • READ_ONLY 읽기 전용. 데이터 변경 시도 시 에러 발생. 가장 가볍고 빠름
  • SCROLL_LOCKS 커서가 포인트한 행을 잠금(Lock) 처리. 데이터 일관성 유지
  • OPTIMISTIC 잠금 없이 사용자가 데이터를 변경할 때 변경 여부를 체크해서 업데이트 시 충돌 여부 판단 (낙관적 동시성 제어)

6. FETCH 이동 방향 키워드

FETCH [ NEXT | PRIOR | FIRST | LAST | ABSOLUTE n | RELATIVE n ] FROM cursor_name;
  • NEXT 다음 행 (기본)
  • PRIOR 이전 행
  • FIRST 첫 행
  • LAST 마지막 행
  • ABSOLUTE n 특정 행 번호로 이동 (1부터 시작)
  • RELATIVE n 현재 위치에서 n만큼 이동 (양수는 앞으로, 음수는 뒤로)

scroll 옵션이 없으면 위 옵션 중에서 NEXT만 사용 가능.

7. ISO 관련 – 트랜잭션 격리 수준과 커서

커서 자체는 ISO 트랜잭션 격리 수준을 설정하지 않지만, 커서 내에서 실행되는 SELECT 문은 세션의 격리 수준의 영향을 받는다.

SET TRANSACTION ISOLATION LEVEL 
    READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SNAPSHOT | SERIALIZABLE;

8. CLOSE, DEALLOCATE

  • CLOSE cursor_name; – 커서를 닫지만 메모리는 유지
  • DEALLOCATE cursor_name; – 커서를 메모리에서 제거 (종료 후 항상 해주는 것이 좋음)

- 예시

DECLARE my_cursor CURSOR LOCAL STATIC READ_ONLY FOR
SELECT name FROM sys.objects;

OPEN my_cursor;

FETCH NEXT FROM my_cursor INTO @name;

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT @name;
    FETCH NEXT FROM my_cursor INTO @name;
END

CLOSE my_cursor;
DEALLOCATE my_cursor;